src/EditProductTab5.cpp

Mon, 18 Jul 2022 17:04:02 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 18 Jul 2022 17:04:02 +0200
changeset 359
dfbb012c631c
parent 305
35ce719998e1
child 435
6f84ab6125ad
permissions
-rw-r--r--

Redesign of the water tabs in product and recipe editors. Prepare for sparge water salt additions. Acid additions are now automatic or manual for mash and sparge. Fixed error in acid calculation strength. Fixed phophoric SG value.

/**
 * EditProduct.cpp is part of bmsapp.
 *
 * tab 5, miscs.
 *
 * bmsapp is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * bmsapp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


bool EditProduct::misc_sort_test(const Miscs &D1, const Miscs &D2)
{
    if (D1.use_use > D2.use_use)
	return false;
    if (D1.use_use < D2.use_use)
	return true;
    if (D1.type > D2.type)
	return false;
    if (D1.type < D2.type)
	return true;
    return (D1.amount > D2.amount);
}


bool EditProduct::block_misc(int stage, int use_use)
{
    if (stage > PROD_STAGE_TERTIARY && use_use < MISC_USES_BOTTLING)
	return true;
    if (stage > PROD_STAGE_PRIMARY && use_use < MISC_USES_SECONDARY)
	return true;
    if (stage > PROD_STAGE_BREW && use_use < MISC_USES_PRIMARY)
	return true;
    if (stage > PROD_STAGE_WAIT && use_use < MISC_USES_MASH)
	return true;
    return false;
}


void EditProduct::refreshMiscs()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    QTableWidgetItem *item;

    qDebug() << "refreshMiscs" << product->miscs.size();
    std::sort(product->miscs.begin(), product->miscs.end(), misc_sort_test);

    const QSignalBlocker blocker1(ui->bs_cacl2Edit);
    const QSignalBlocker blocker2(ui->bs_caso4Edit);
    const QSignalBlocker blocker3(ui->bs_mgso4Edit);
    const QSignalBlocker blocker4(ui->bs_naclEdit);
    const QSignalBlocker blocker5(ui->bs_mgcl2Edit);
    const QSignalBlocker blocker6(ui->bs_nahco3Edit);
    const QSignalBlocker blocker7(ui->bs_caco3Edit);
    const QSignalBlocker blocker8(ui->mw_acidPick);
    const QSignalBlocker blocker9(ui->mw_acidvolEdit);
    const QSignalBlocker blocker10(ui->ss_cacl2Edit);
    const QSignalBlocker blocker11(ui->ss_caso4Edit);
    const QSignalBlocker blocker12(ui->ss_mgso4Edit);
    const QSignalBlocker blocker13(ui->ss_naclEdit);
    const QSignalBlocker blocker14(ui->ss_mgcl2Edit);
    const QSignalBlocker blocker15(ui->sp_acidtypeEdit);
    const QSignalBlocker blocker16(ui->sp_acidvolEdit);

    const QStringList labels({tr("Ingredient"), tr("Type"), tr("Use at"), tr("Time"), tr("Amount"), tr("Stock"), tr("Delete"), tr("Edit") });

    ui->miscsTable->setColumnCount(8);
    ui->miscsTable->setColumnWidth(0, 250);	/* Ingredient	*/
    ui->miscsTable->setColumnWidth(1,  90);	/* Type		*/
    ui->miscsTable->setColumnWidth(2,  90);	/* Added	*/
    ui->miscsTable->setColumnWidth(3,  75);	/* Time		*/
    ui->miscsTable->setColumnWidth(4,  80);	/* Amount	*/
    ui->miscsTable->setColumnWidth(5,  80);	/* Stock	*/
    ui->miscsTable->setColumnWidth(6,  80);	/* Delete	*/
    ui->miscsTable->setColumnWidth(7,  80);	/* Edit		*/
    ui->miscsTable->setHorizontalHeaderLabels(labels);
    ui->miscsTable->verticalHeader()->hide();
    ui->miscsTable->setRowCount(product->miscs.size());

    for (int i = 0; i < product->miscs.size(); i++) {

	ui->miscsTable->setItem(i, 0, new QTableWidgetItem(product->miscs.at(i).name));

	item = new QTableWidgetItem(QCoreApplication::translate("MiscType", g_misc_types[product->miscs.at(i).type]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->miscsTable->setItem(i, 1, item);

	item = new QTableWidgetItem(QCoreApplication::translate("MiscUse", g_misc_uses[product->miscs.at(i).use_use]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->miscsTable->setItem(i, 2, item);

	if (product->miscs.at(i).use_use == MISC_USES_BOIL) {
	    item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(i).time, 1, 'f', 0, '0'));
	} else if (product->miscs.at(i).use_use == MISC_USES_PRIMARY || product->miscs.at(i).use_use == MISC_USES_SECONDARY) {
	    item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(i).time / 1440, 1, 'f', 0, '0'));
	} else {
	    item = new QTableWidgetItem(QString(""));
	}
	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->miscsTable->setItem(i, 3, item);

	if (product->miscs.at(i).amount_is_weight)
	    item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(i).amount * 1000.0, 3, 'f', 2, '0'));
	else
	    item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(i).amount * 1000.0, 3, 'f', 2, '0'));
	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->miscsTable->setItem(i, 4, item);

	if (block_misc(product->stage, product->miscs.at(i).use_use)) {
	    item = new QTableWidgetItem(QString(""));
	} else {
	    if (product->miscs.at(i).amount_is_weight)
            	item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(i).inventory * 1000.0, 3, 'f', 2, '0'));
            else
            	item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(i).inventory * 1000.0, 3, 'f', 2, '0'));
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
	    if (product->miscs.at(i).inventory < product->miscs.at(i).amount)
            	item->setForeground(QBrush(QColor(Qt::red)));
	}
        ui->miscsTable->setItem(i, 5, item);

	/*
	 * Add the Delete and Edit row buttons.
	 * Not for water agents, these are set on the water tab.
	 * And also not if the ingredient has been used.
	 */
	if (product->miscs.at(i).type == MISC_TYPES_WATER_AGENT) {
	    ui->miscsTable->removeCellWidget(i, 6);	/* to remove the unneeded button */
	    item = new QTableWidgetItem("");
            item->setToolTip(tr("Edit this from the water tab"));
            ui->miscsTable->setItem(i, 6, item);
	    ui->miscsTable->removeCellWidget(i, 7);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Edit this from the water tab"));
            ui->miscsTable->setItem(i, 7, item);
	} else if (block_misc(product->stage, product->miscs.at(i).use_use)) {
	    ui->miscsTable->removeCellWidget(i, 6);     /* to remove the unneeded button */
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Misc already used"));
            ui->miscsTable->setItem(i, 6, item);
            ui->miscsTable->removeCellWidget(i, 7);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Misc already used"));
            ui->miscsTable->setItem(i, 7, item);
	} else {
	    if (ui->miscsTable->item(i, 6)) {
		ui->miscsTable->takeItem(i, 6);		/* to remove the old tooltip */
	    }
	    if (ui->miscsTable->item(i, 7)) {
		ui->miscsTable->takeItem(i, 7);
	    }
            pWidget = new QWidget();
            QPushButton* btn_dele = new QPushButton();
            btn_dele->setObjectName(QString("%1").arg(i));  /* Send row with the button */
            btn_dele->setText(tr("Delete"));
            connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteMiscRow_clicked()));
            pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_dele);
            pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->miscsTable->setCellWidget(i, 6, pWidget);

            pWidget = new QWidget();
            QPushButton* btn_edit = new QPushButton();
            btn_edit->setObjectName(QString("%1").arg(i));  /* Send row with the button */
            btn_edit->setText(tr("Edit"));
            connect(btn_edit, SIGNAL(clicked()), this, SLOT(editMiscRow_clicked()));
            pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_edit);
            pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->miscsTable->setCellWidget(i, 7, pWidget);
	}

	/*
	 * Update the water agents.
	 */
	if (product->miscs.at(i).type == MISC_TYPES_WATER_AGENT && product->miscs.at(i).use_use == MISC_USES_MASH) {
	    if (product->miscs.at(i).name == "CaCl2") {
		ui->bs_cacl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "CaSO4") {
		ui->bs_caso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "MgSO4") {
		ui->bs_mgso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "NaCl") {
		ui->bs_naclEdit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "MgCl2") {
		ui->bs_mgcl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "NaHCO3") {
		ui->bs_nahco3Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "CaCO3") {
		ui->bs_caco3Edit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "Melkzuur" || product->miscs.at(i).name == "Lactic") {
		product->wa_acid_name = 0;
		product->wa_acid_perc = my_acids.at(0).AcidPrc;
		ui->mw_acidPick->setCurrentIndex(0);
		ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
		ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "Zoutzuur" || product->miscs.at(i).name == "Hydrochloric") {
                product->wa_acid_name = 1;
                product->wa_acid_perc = my_acids.at(1).AcidPrc;
		ui->mw_acidPick->setCurrentIndex(1);
                ui->mw_acidpercEdit->setValue(my_acids.at(1).AcidPrc);
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "Fosforzuur" || product->miscs.at(i).name == "Phosphoric") {
                product->wa_acid_name = 2;
                product->wa_acid_perc = my_acids.at(2).AcidPrc;
		ui->mw_acidPick->setCurrentIndex(2);
                ui->mw_acidpercEdit->setValue(my_acids.at(2).AcidPrc);
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
	    } else if (product->miscs.at(i).name == "Zwavelzuur" || product->miscs.at(i).name == "Sulfuric") {
                product->wa_acid_name = 3;
                product->wa_acid_perc = my_acids.at(3).AcidPrc;
		ui->mw_acidPick->setCurrentIndex(3);
                ui->mw_acidpercEdit->setValue(my_acids.at(3).AcidPrc);
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
	    }
	} else if (product->miscs.at(i).type == MISC_TYPES_WATER_AGENT && product->miscs.at(i).use_use == MISC_USES_SPARGE) {
            if (product->miscs.at(i).name == "CaCl2") {
                ui->ss_cacl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "CaSO4") {
                ui->ss_caso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgSO4") {
                ui->ss_mgso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "NaCl") {
                ui->ss_naclEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgCl2") {
                ui->ss_mgcl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Melkzuur" || product->miscs.at(i).name == "Lactic") {
                product->sparge_acid_type = 0;
                product->sparge_acid_perc = my_acids.at(0).AcidPrc;
                ui->sp_acidtypeEdit->setCurrentIndex(0);
                ui->sp_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zoutzuur" || product->miscs.at(i).name == "Hydrochloric") {
                product->sparge_acid_type = 1;
                product->sparge_acid_perc = my_acids.at(1).AcidPrc;
                ui->sp_acidtypeEdit->setCurrentIndex(1);
                ui->sp_acidpercEdit->setValue(my_acids.at(1).AcidPrc);
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Fosforzuur" || product->miscs.at(i).name == "Phosphoric") {
                product->sparge_acid_type = 2;
                product->sparge_acid_perc = my_acids.at(2).AcidPrc;
                ui->sp_acidtypeEdit->setCurrentIndex(2);
                ui->sp_acidpercEdit->setValue(my_acids.at(2).AcidPrc);
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zwavelzuur" || product->miscs.at(i).name == "Sulfuric") {
                product->sparge_acid_type = 3;
                product->sparge_acid_perc = my_acids.at(3).AcidPrc;
                ui->sp_acidtypeEdit->setCurrentIndex(3);
                ui->sp_acidpercEdit->setValue(my_acids.at(3).AcidPrc);
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            }
        }
    }
}


void EditProduct::calcMiscs()
{
    product->miscs_ok = true;
    for (int i = 0; i < product->miscs.size(); i++) {
	if ((((product->inventory_reduced <= PROD_STAGE_BREW)     && (product->miscs.at(i).use_use <= MISC_USES_BOIL)) ||  // Starter, Mash, Boil
	     ((product->inventory_reduced <= PROD_STAGE_BREW)     && (product->miscs.at(i).use_use == MISC_USES_SPARGE)) ||
             ((product->inventory_reduced <= PROD_STAGE_PRIMARY)  && (product->miscs.at(i).use_use == MISC_USES_PRIMARY)) ||
             ((product->inventory_reduced <= PROD_STAGE_TERTIARY) && (product->miscs.at(i).use_use == MISC_USES_SECONDARY)) ||
             ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->miscs.at(i).use_use == MISC_USES_BOTTLING))) &&
              (product->miscs.at(i).inventory < product->miscs.at(i).amount)) {
	    product->miscs_ok = false;
	}
    }
}


/*
 * Manipulate the memory array and update the miscs table.
 */
void EditProduct::brewing_salt_sub(QString salt, double val, int use)
{
    QTableWidgetItem *item;

    val = round(val * 100.0) / 100.0;
    if (val == 0) {
	/*
	 * Remove this salt if it is in the table.
	 */
	for (int i = 0; i < product->miscs.size(); i++) {
	    if (salt.contains(product->miscs.at(i).name) && product->miscs.at(i).use_use == use) {
		qDebug() << "  brewing_salt_sub delete" << salt << use;
		product->miscs.removeAt(i);
		refreshMiscs();
		return;
	    }
	}
	qDebug() << "  brewing_salt_sub delete error";
	return;
    }

    /*
     * First see if this salt is in the table.
     * If it is, update the amount.
     */
    for (int i = 0; i < product->miscs.size(); i++) {
	if (salt.contains(product->miscs.at(i).name) && product->miscs.at(i).use_use == use) {
	    product->miscs[i].amount = val / 1000.0;
	    if (product->miscs.at(i).amount_is_weight)
            	item = new QTableWidgetItem(QString("%1 gr").arg(val, 3, 'f', 2, '0'));
            else
	    	item = new QTableWidgetItem(QString("%1 ml").arg(val, 3, 'f', 2, '0'));
	    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
	    ui->miscsTable->setItem(i, 4, item);
	    return;
	}
    }

    /*
     * We need a new entry. Search in the database is tricky because
     * we are here with possible more names for the same salt. The
     * name can be like 'Lactic Melkzuur'. So we select only the
     * brewing salts and manually check their names.
     */
    QSqlQuery query("SELECT * FROM inventory_miscs WHERE type = 4");
    while (query.next()) {
	if (salt.contains(query.value(1).toString())) {
	    qDebug() << "  found it, append";
	    Miscs m;
	    m.name = query.value(1).toString();
            m.amount = val / 1000.0;
            m.type = query.value(2).toInt();
            m.use_use = use;
            m.time = query.value(4).toDouble();
            m.amount_is_weight = query.value(5).toInt() ? true:false;
            m.cost = query.value(10).toDouble();
	    m.inventory = query.value(9).toDouble();
            product->miscs.append(m);
	    refreshMiscs();
	    return;
	}
    }

    qDebug() << "brewing_salt_sub, nothing done." << salt << val << use;
}


/*
 * Edit brewing salt and recalculate.
 */
void EditProduct::set_brewing_salt(QString salt, double val, int use)
{
    val = round(val * 100.0) / 100.0;
    qDebug() << "set_brewing_salt" << salt << val << use;
    brewing_salt_sub(salt, val, use);
    calcWater();
    is_changed();
}


void EditProduct::addMiscRow_clicked()
{
    Miscs newm;

    for (int i = 0; i < product->miscs.size(); i++) {
        if (product->miscs.at(i).amount == 0)
            return;     // Add only one at a time.
    }

    newm.name = "Select one";
    newm.amount = 0;
    newm.type = MISC_TYPES_SPICE;
    newm.use_use = MISC_USES_STARTER;
    newm.time = 0;
    newm.amount_is_weight = true;
    newm.cost = 0;
    product->miscs.append(newm);
    is_changed();
    refreshMiscs();
}


void EditProduct::deleteMiscRow_clicked()
{
    if (product->locked || product->miscs.size() < 1)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Delete misc row" << row << product->miscs.size();

    int rc = QMessageBox::warning(this, tr("Delete misc"), tr("Delete %1").arg(product->miscs.at(row).name),
                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if (rc == QMessageBox::No)
        return;

    product->miscs.removeAt(row);
    is_changed();
    emit refreshAll();
}


void EditProduct::misc_amount_changed(double val)
{
    QTableWidgetItem *item;

    qDebug() << "misc_amount_changed()" << product->miscs_row << val;

    product->miscs[product->miscs_row].amount = val / 1000.0;
    if (product->miscs.at(product->miscs_row).amount_is_weight) {
        item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(product->miscs_row).amount * 1000.0, 3, 'f', 2, '0'));
    } else {
        item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(product->miscs_row).amount * 1000.0, 3, 'f', 2, '0'));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 4, item);

    is_changed();
}


void EditProduct::misc_time_changed(int val)
{
    QTableWidgetItem *item;

    qDebug() << "misc_time_changed()" << product->miscs_row << val;

    if (product->miscs.at(product->miscs_row).use_use == MISC_USES_BOIL) {
	product->miscs[product->miscs_row].time = val;
	item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
    } else if (product->miscs.at(product->miscs_row).use_use == MISC_USES_PRIMARY ||
	       product->miscs.at(product->miscs_row).use_use == MISC_USES_SECONDARY) {
	product->miscs[product->miscs_row].time = val * 1440;
	item = new QTableWidgetItem(QString("%1 days.").arg(val, 1, 'f', 0, '0'));
    } else {
	item = new QTableWidgetItem(QString(""));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 3, item);

    is_changed();
}


void EditProduct::misc_select_changed(int val)
{
    QSqlQuery query;
    bool instock = minstockEdit->isChecked();
    QString w;
    QTableWidgetItem *item;

    if (val < 1)
        return;

    qDebug() << "misc_select_changed()" << product->miscs_row << val << instock;

    /*
     * Search the misc ingredient pointed by the index and instock flag.
     */
    QString sql = "SELECT name,type,use_use,time,amount_is_weight,cost,inventory FROM inventory_miscs WHERE ";
    if (instock)
        sql.append("inventory > 0 AND ");
    sql.append("type != 4 ORDER BY name");
    qDebug() << sql;
    query.prepare(sql);
    query.exec();
    query.first();
    for (int i = 0; i < (val - 1); i++) {
        query.next();
    }
    qDebug() << "found" << query.value(0).toString();

    /*
     * Replace the misc record contents
     */
    product->miscs[product->miscs_row].name = query.value(0).toString();
    product->miscs[product->miscs_row].type = query.value(1).toInt();
    product->miscs[product->miscs_row].use_use = query.value(2).toInt();
    product->miscs[product->miscs_row].time = query.value(3).toDouble();
    product->miscs[product->miscs_row].amount_is_weight = query.value(4).toInt() ? true:false;
    product->miscs[product->miscs_row].cost = query.value(5).toDouble();
    product->miscs[product->miscs_row].inventory = query.value(6).toDouble();

    /*
     * Update the visible fields
     */
    mnameEdit->setText(product->miscs.at(product->miscs_row).name);
    ui->miscsTable->setItem(product->miscs_row, 0, new QTableWidgetItem(product->miscs.at(product->miscs_row).name));

    item = new QTableWidgetItem(QCoreApplication::translate("MiscType", g_misc_types[product->miscs.at(product->miscs_row).type]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 1, item);

    item = new QTableWidgetItem(QCoreApplication::translate("MiscUse", g_misc_uses[product->miscs.at(product->miscs_row).use_use]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 2, item);
    useatEdit->setCurrentIndex(product->miscs.at(product->miscs_row).use_use);

    if (product->miscs.at(product->miscs_row).use_use == 3 || product->miscs.at(product->miscs_row).use_use == 4) {     // Fermentation stages
        mtimeEdit->setValue(product->miscs.at(product->miscs_row).time / 1440);
        mtimeEdit->setReadOnly(false);
	mtimeLabel->setText(tr("Time in days:"));
	item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(product->miscs_row).time / 1440, 1, 'f', 0, '0'));
    } else if (product->miscs.at(product->miscs_row).use_use == 2) {    // Boil
        mtimeEdit->setValue(product->miscs.at(product->miscs_row).time);
        mtimeEdit->setReadOnly(false);
	mtimeLabel->setText(tr("Time in minutes:"));
	item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(product->miscs_row).time, 1, 'f', 0, '0'));
    } else {
        mtimeEdit->setReadOnly(true);
	mtimeLabel->setText("");
	item = new QTableWidgetItem(QString(""));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 3, item);

    if (product->miscs.at(product->miscs_row).amount_is_weight) {
	mamountLabel->setText(tr("Amount in gr:"));
	item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(product->miscs_row).amount * 1000.0, 3, 'f', 2, '0'));
    } else {
	mamountLabel->setText(tr("Amount in ml:"));
	item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(product->miscs_row).amount * 1000.0, 3, 'f', 2, '0'));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 4, item);

    if (product->miscs.at(product->miscs_row).amount_is_weight)
	item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(product->miscs_row).inventory * 1000.0, 3, 'f', 2, '0'));
    else
	item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(product->miscs_row).inventory * 1000.0, 3, 'f', 2, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    if (product->miscs.at(product->miscs_row).inventory < product->miscs.at(product->miscs_row).amount)
	item->setForeground(QBrush(QColor(Qt::red)));
    ui->miscsTable->setItem(product->miscs_row, 5, item);

    is_changed();
    qDebug() << "before" << product->miscs_row;
    refreshMiscs();
    /*
     * The order of the list is changed, lookup the item we just added.
     */
    for (int i = 0; i < product->miscs.size(); i++) {
	if ((product->miscs.at(i).name == query.value(0).toString()) &&
	    (product->miscs.at(i).type == query.value(1).toInt()) &&
	    (product->miscs.at(i).use_use == query.value(2).toInt())) {
	    product->miscs_row = i;
	    break;
	}
    }
    qDebug() << "after" << product->miscs_row;
}


void EditProduct::misc_instock_changed(bool val)
{
    QSqlQuery query;

    qDebug() << "misc_instock_changed()" << product->miscs_row << val;

    this->mselectEdit->setCurrentIndex(-1);
    this->mselectEdit->clear();
    QString sql = "SELECT name,type,amount_is_weight,inventory FROM inventory_miscs WHERE ";
    if (val)
        sql.append("inventory > 0 AND ");
    sql.append("type != 4 ORDER BY name");
    query.prepare(sql);
    query.exec();
    query.first();
    this->mselectEdit->addItem("");      // Start with empty value
    for (int i = 0; i < query.size(); i++) {
        this->mselectEdit->addItem(query.value(0).toString()+ " (" + QCoreApplication::translate("MiscType", g_misc_types[query.value(1).toInt()]) + ") " +
                        QString("%1 %2").arg(query.value(3).toDouble() * 1000.0, 3, 'f', 2, '0').arg(query.value(2).toInt()?"gr":"ml"));
        query.next();
    }
}


void EditProduct::misc_useat_changed(int val)
{
    QTableWidgetItem *item;

    qDebug() << "misc_useat_changed" << val;
    product->miscs[product->miscs_row].use_use = val;
    item = new QTableWidgetItem(QCoreApplication::translate("MiscUse", g_misc_uses[val]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 2, item);

    if (val == MISC_USES_PRIMARY || val == MISC_USES_SECONDARY) {
	product->miscs[product->miscs_row].time = mtimeEdit->value() * 1440;
        mtimeEdit->setReadOnly(false);
        mtimeLabel->setText(tr("Time in days:"));
        item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(product->miscs_row).time / 1440, 1, 'f', 0, '0'));
    } else if (val == MISC_USES_BOIL) {
	product->miscs[product->miscs_row].time = mtimeEdit->value();
        mtimeEdit->setReadOnly(false);
        mtimeLabel->setText(tr("Time in minutes:"));
        item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(product->miscs_row).time, 1, 'f', 0, '0'));
    } else {
	product->miscs[product->miscs_row].time = 0;
	mtimeEdit->setValue(0);
        mtimeEdit->setReadOnly(true);
        mtimeLabel->setText("");
        item = new QTableWidgetItem(QString(""));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->miscsTable->setItem(product->miscs_row, 3, item);

    is_changed();
}


void EditProduct::editMiscRow_clicked()
{
    QSqlQuery query;

    if (product->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    product->miscs_row = pb->objectName().toInt();
    qDebug() << "Edit misc row" << product->miscs_row;
    Miscs backup = product->miscs.at(product->miscs_row);

    QDialog* dialog = new QDialog(this);
    dialog->resize(738, 230);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(30, 180, 671, 32));
    buttonBox->setLayoutDirection(Qt::LeftToRight);
    buttonBox->setOrientation(Qt::Horizontal);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
    buttonBox->setCenterButtons(true);

    QLabel *nameLabel = new QLabel(dialog);
    nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
    nameLabel->setText(tr("Current ingredient:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    mamountLabel = new QLabel(dialog);
    mamountLabel->setObjectName(QString::fromUtf8("mamountLabel"));
    if (product->miscs.at(product->miscs_row).amount_is_weight)
    	mamountLabel->setText(tr("Amount in gr:"));
    else
	mamountLabel->setText(tr("Amount in ml:"));
    mamountLabel->setGeometry(QRect(10, 70, 141, 20));
    mamountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    mtimeLabel = new QLabel(dialog);
    mtimeLabel->setObjectName(QString::fromUtf8("mtimeLabel"));
    if (product->miscs.at(product->miscs_row).use_use == MISC_USES_PRIMARY || product->miscs.at(product->miscs_row).use_use == MISC_USES_SECONDARY)
        mtimeLabel->setText(tr("Time in days:"));
    else if (product->miscs.at(product->miscs_row).use_use == MISC_USES_BOIL)
        mtimeLabel->setText(tr("Time in minutes:"));
    else
        mtimeLabel->setText("");
    mtimeLabel->setGeometry(QRect(10, 100, 141, 20));
    mtimeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    QLabel *selectLabel = new QLabel(dialog);
    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
    selectLabel->setText(tr("Select ingredient:"));
    selectLabel->setGeometry(QRect(10, 40, 141, 20));
    selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *instockLabel = new QLabel(dialog);
    instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
    instockLabel->setText(tr("In stock:"));
    instockLabel->setGeometry(QRect(525, 40, 121, 20));
    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    mselectEdit = new QComboBox(dialog);
    mselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
    mselectEdit->setGeometry(QRect(160, 40, 371, 23));

    mnameEdit = new QLineEdit(dialog);
    mnameEdit->setObjectName(QString::fromUtf8("mnameEdit"));
    mnameEdit->setText(product->miscs.at(product->miscs_row).name);
    mnameEdit->setGeometry(QRect(160, 10, 511, 23));
    mnameEdit->setReadOnly(true);

    mamountEdit = new QDoubleSpinBox(dialog);
    mamountEdit->setObjectName(QString::fromUtf8("mamountEdit"));
    mamountEdit->setGeometry(QRect(160, 70, 121, 24));
    mamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    mamountEdit->setAccelerated(true);
    mamountEdit->setDecimals(2);
    mamountEdit->setMaximum(1000000.0);
    mamountEdit->setSingleStep(0.1);
    mamountEdit->setValue(product->miscs.at(product->miscs_row).amount * 1000.0);

    mtimeEdit = new QSpinBox(dialog);
    mtimeEdit->setObjectName(QString::fromUtf8("mtimeEdit"));
    mtimeEdit->setGeometry(QRect(160, 100, 121, 24));
    mtimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    mtimeEdit->setAccelerated(true);
    mtimeEdit->setMaximum(10000.0);
    if (product->miscs.at(product->miscs_row).use_use == MISC_USES_PRIMARY || product->miscs.at(product->miscs_row).use_use == MISC_USES_SECONDARY) {
	mtimeEdit->setValue(product->miscs.at(product->miscs_row).time / 1440);
        mtimeEdit->setReadOnly(false);
    } else if (product->miscs.at(product->miscs_row).use_use == MISC_USES_BOIL) {
	mtimeEdit->setValue(product->miscs.at(product->miscs_row).time);
        mtimeEdit->setReadOnly(false);
    } else {
	mtimeEdit->setReadOnly(true);
    }

    useatEdit = new QComboBox(dialog);
    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
    useatEdit->setGeometry(QRect(160, 130, 161, 23));
    useatEdit->addItem(tr("Starter"));
    useatEdit->addItem(tr("Mash"));
    useatEdit->addItem(tr("Boil"));
    useatEdit->addItem(tr("Primary"));
    useatEdit->addItem(tr("Secondary"));
    useatEdit->addItem(tr("Bottling"));
    useatEdit->addItem(tr("Sparge"));
    useatEdit->setCurrentIndex(product->miscs.at(product->miscs_row).use_use);

    minstockEdit = new QCheckBox(dialog);
    minstockEdit->setObjectName(QString::fromUtf8("minstockEdit"));
    minstockEdit->setGeometry(QRect(655, 40, 85, 21));
    minstockEdit->setChecked(true);

    misc_instock_changed(true);

    connect(mselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::misc_select_changed);
    connect(mamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::misc_amount_changed);
    connect(mtimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::misc_time_changed);
    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::misc_useat_changed);
    connect(minstockEdit, &QCheckBox::stateChanged, this, &EditProduct::misc_instock_changed);
    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));

    dialog->setModal(true);
    dialog->exec();
    if (dialog->result() == QDialog::Rejected) {
        qDebug() << "reject and rollback";
        product->miscs[product->miscs_row] = backup;
    } else {
        /* Clear time if misc is not used for boil or fermentation. */
        if (! (product->miscs.at(product->miscs_row).use_use == MISC_USES_BOIL ||
               product->miscs.at(product->miscs_row).use_use == MISC_USES_PRIMARY ||
               product->miscs.at(product->miscs_row).use_use == MISC_USES_SECONDARY)) {
            if (product->miscs.at(product->miscs_row).time) {
                product->miscs[product->miscs_row].time = 0;
                is_changed();
            }
        }
    }

    disconnect(mselectEdit, nullptr, nullptr, nullptr);
    disconnect(mamountEdit, nullptr, nullptr, nullptr);
    disconnect(mtimeEdit, nullptr, nullptr, nullptr);
    disconnect(useatEdit, nullptr, nullptr, nullptr);
    disconnect(minstockEdit, nullptr, nullptr, nullptr);
    disconnect(buttonBox, nullptr, nullptr, nullptr);

    emit refreshAll();
}


void EditProduct::adjustMiscs(double factor)
{
    double amount;

    if (product->miscs.size() == 0)
	return;

    const QSignalBlocker blocker1(ui->bs_cacl2Edit);
    const QSignalBlocker blocker2(ui->bs_caso4Edit);
    const QSignalBlocker blocker3(ui->bs_mgso4Edit);
    const QSignalBlocker blocker4(ui->bs_naclEdit);
    const QSignalBlocker blocker5(ui->bs_mgcl2Edit);
    const QSignalBlocker blocker6(ui->bs_nahco3Edit);
    const QSignalBlocker blocker7(ui->bs_caco3Edit);
    const QSignalBlocker blocker9(ui->mw_acidvolEdit);
    const QSignalBlocker blocker10(ui->ss_cacl2Edit);
    const QSignalBlocker blocker11(ui->ss_caso4Edit);
    const QSignalBlocker blocker12(ui->ss_mgso4Edit);
    const QSignalBlocker blocker13(ui->ss_naclEdit);
    const QSignalBlocker blocker14(ui->ss_mgcl2Edit);

    for (int i = 0; i < product->miscs.size(); i++) {
	amount = product->miscs.at(i).amount * factor;
	product->miscs[i].amount = amount;

	/*
         * Update the water agents.
         */
        if (product->miscs.at(i).type == MISC_TYPES_WATER_AGENT && product->miscs.at(i).use_use == MISC_USES_MASH) {
            if (product->miscs.at(i).name == "CaCl2") {
                ui->bs_cacl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "CaSO4") {
                ui->bs_caso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgSO4") {
                ui->bs_mgso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "NaCl") {
                ui->bs_naclEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgCl2") {
                ui->bs_mgcl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "NaHCO3") {
                ui->bs_nahco3Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "CaCO3") {
                ui->bs_caco3Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Melkzuur" || product->miscs.at(i).name == "Lactic") {
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zoutzuur" || product->miscs.at(i).name == "Hydrochloric") {
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Fosforzuur" || product->miscs.at(i).name == "Phosphoric") {
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zwavelzuur" || product->miscs.at(i).name == "Sulfuric") {
                ui->mw_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            }
        } else if (product->miscs.at(i).type == MISC_TYPES_WATER_AGENT && product->miscs.at(i).use_use == MISC_USES_SPARGE) {
            if (product->miscs.at(i).name == "CaCl2") {
                ui->ss_cacl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "CaSO4") {
                ui->ss_caso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgSO4") {
                ui->ss_mgso4Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "NaCl") {
                ui->ss_naclEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "MgCl2") {
                ui->ss_mgcl2Edit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Melkzuur" || product->miscs.at(i).name == "Lactic") {
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zoutzuur" || product->miscs.at(i).name == "Hydrochloric") {
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Fosforzuur" || product->miscs.at(i).name == "Phosphoric") {
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            } else if (product->miscs.at(i).name == "Zwavelzuur" || product->miscs.at(i).name == "Sulfuric") {
                ui->sp_acidvolEdit->setValue(product->miscs.at(i).amount * 1000.0);
            }
        }
    }
}

mercurial