src/EditProductTab6.cpp

changeset 175
f1ed3a2a94e9
child 179
512f492358e3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab6.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,593 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * tab 6, yeasts.
+ *
+ * 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 yeaste 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::yeast_sort_test(const Yeasts &D1, const Yeasts &D2)
+{
+    if (D1.y_use > D2.y_use)
+	return false;
+    if (D1.y_use < D2.y_use)
+	return true;
+    return (D1.y_amount > D2.y_amount);
+}
+
+
+void EditProduct::refreshYeasts()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshYeasts" << product->yeasts.size();
+    std::sort(product->yeasts.begin(), product->yeasts.end(), yeast_sort_test);
+
+    const QStringList labels({tr("Yeast"), tr("Laboratory"), tr("Code"), tr("Type"), tr("Use for"), tr("Min. °C"), tr("Max. °C"),
+		              tr("Tol. %"), tr("Attn. %"), tr("Amount"), tr("Delete"), tr("Edit") });
+
+    ui->yeastsTable->setColumnCount(12);
+    ui->yeastsTable->setColumnWidth(0, 200);	/* Yeast	*/
+    ui->yeastsTable->setColumnWidth(1, 125);	/* Laboratory	*/
+    ui->yeastsTable->setColumnWidth(2,  80);	/* Code		*/
+    ui->yeastsTable->setColumnWidth(3,  80);	/* Type		*/
+    ui->yeastsTable->setColumnWidth(4, 100);	/* Usage	*/
+    ui->yeastsTable->setColumnWidth(5,  60);	/* Min. 	*/
+    ui->yeastsTable->setColumnWidth(6,  60);	/* Max.		*/
+    ui->yeastsTable->setColumnWidth(7,  60);	/* Tolerance	*/
+    ui->yeastsTable->setColumnWidth(8,  60);	/* Attenuation	*/
+    ui->yeastsTable->setColumnWidth(9,  90);	/* Amount	*/
+    ui->yeastsTable->setColumnWidth(10, 80);	/* Delete	*/
+    ui->yeastsTable->setColumnWidth(11, 80);	/* Edit		*/
+    ui->yeastsTable->setHorizontalHeaderLabels(labels);
+    ui->yeastsTable->verticalHeader()->hide();
+    ui->yeastsTable->setRowCount(product->yeasts.size());
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+
+	ui->yeastsTable->setItem(i, 0, new QTableWidgetItem(product->yeasts.at(i).y_name));
+	ui->yeastsTable->setItem(i, 1, new QTableWidgetItem(product->yeasts.at(i).y_laboratory));
+	ui->yeastsTable->setItem(i, 2, new QTableWidgetItem(product->yeasts.at(i).y_product_id));
+
+	item = new QTableWidgetItem(yeast_forms[product->yeasts.at(i).y_form]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 3, item);
+
+        item = new QTableWidgetItem(yeast_use[product->yeasts.at(i).y_use]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 4, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_min_temperature, 2, 'f', 1, '0'));
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 5, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_max_temperature, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 6, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_tolerance, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 7, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_attenuation, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 8, item);
+
+	if (product->yeasts.at(i).y_form == 0)
+            item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).y_amount, 1, 'f', 0, '0'));
+	else if (product->yeasts.at(i).y_form == 1)
+	    item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0'));
+	else
+	    item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 9, item);
+
+	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(deleteYeastRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_dele);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->yeastsTable->setCellWidget(i, 10, 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(editYeastRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_edit);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->yeastsTable->setCellWidget(i, 11, pWidget);
+    }
+}
+
+
+/*
+ * The results are not stored line in EditProduct. This is just a hint.
+ */
+void EditProduct::calcYeast()
+{
+    double sg = product->est_og;
+    double plato = Utils::sg_to_plato(sg);
+    double volume = product->batch_size * 0.9;	// Volume min trub chiller loss.
+    bool maybe_starter = false;
+    double pitchrate = 0.75;
+    double initcells = 0;
+
+    qDebug() << "calcYeast()";
+    ui->yeastProcedure->setCurrentIndex(0);
+
+    if (product->yeasts.size() == 0)
+	return;		// No yeast in product.
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+	if (product->yeasts.at(i).y_use == 0) {		// Primary
+	    if (product->yeasts.at(i).y_form == 1) {
+		/*
+		 * Dry yeast, build the formule with the yeast parameters.
+		 * Based on https://www.lallemandbrewing.com/en/canada/brewers-corner/brewing-tools/pitching-rate-calculator/
+		 */
+		ui->yeastProcedure->setCurrentIndex(2);
+		ui->lo_gr_hlEdit->setValue(product->yeasts.at(i).y_gr_hl_lo);
+		ui->hi_gr_hlEdit->setValue(product->yeasts.at(i).y_gr_hl_hi);
+		ui->lo_sgEdit->setValue(product->yeasts.at(i).y_sg_lo);
+		ui->hi_sgEdit->setValue(product->yeasts.at(i).y_sg_hi);
+		double og = product->yeasts.at(i).y_sg_lo;
+		double f1 = product->yeasts.at(i).y_gr_hl_lo / 100.0;
+		double f2 = round(f1 / 5 * 1000000.0) / 1000000.0;
+     		double multiplier = (sg <= og) ? f1 : (f1 + f2 * (sg - og) / 0.008);
+		qDebug() << "  sg:" << sg << "og:" << og << "f1:" << f1 << "f2:" << f2 << "multiplier:" << multiplier;
+     		double yeast_grams = round(volume * multiplier * 100.0) / 100.0; // * (100 / dataRecord.starter_viability), 2);
+     		double yeast_gr_hl = round((yeast_grams / (volume * 0.01)) * 100.0) / 100.0;
+		ui->need_grEdit->setValue(yeast_grams);
+		ui->pitch_grEdit->setValue(yeast_gr_hl);
+     		qDebug() << "  need" << yeast_grams << "grams, gr/hl:" << yeast_gr_hl;
+		return;
+	    } else {
+		/*
+		 * Liquid, slant, culture etc.
+		 * pitchrate see https://www.brewersfriend.com/yeast-pitch-rate-and-starter-calculator/
+		 * and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
+		 */
+		ui->yeastProcedure->setCurrentIndex(1);
+		if (product->yeasts.at(i).y_type == 0) {		// Lager yeast
+		    pitchrate = 1.5;
+		    if (sg > 1.060)
+			pitchrate = 2.0;
+		} else if (product->yeasts.at(i).y_type == 6) {	// Real Kveik
+		    pitchrate = 0.075;
+		} else {
+		    pitchrate = 0.75;
+		    if (sg > 1.060)
+			pitchrate = 1.0;
+		}
+		if (product->yeasts.at(i).y_form == 0)
+		    initcells = (product->yeasts.at(i).y_cells / 1000000000) * product->yeasts.at(i).y_amount * 0.97;	// 97% viability assumed.
+		else
+		    initcells = (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * 0.97;
+
+		double needed = round(pitchrate * volume * plato * 10.0) / 10.0;
+		double starter = 0;
+		if (needed > initcells) {
+		    maybe_starter = true;
+		    starter = round(needed / 2.0) / 100.0;	// A very rough starter size estimate.
+		}
+
+		ui->pitchrateEdit->setValue(pitchrate);
+		ui->initcellsEdit->setValue(initcells);
+		ui->targetcellsEdit->setValue(needed);
+		ui->starterEdit->setValue(starter);
+
+		qDebug() << "  pitchrate:" << pitchrate << "needed:" << needed << "initcells:" << initcells << "starter" << maybe_starter << "size" << starter;
+	    }
+	    break;
+	}
+    }
+}
+
+
+void EditProduct::addYeastRow_clicked()
+{
+    Yeasts newy;
+
+    qDebug() << "Add yeast row";
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+        if (product->yeasts.at(i).y_amount == 0)
+            return;     // Add only one at a time.
+    }
+
+    newy.y_name = "Select one";
+    newy.y_laboratory = "";
+    newy.y_product_id = "";
+    newy.y_amount = 0;
+    newy.y_type = 0;
+    newy.y_form = 0;
+    newy.y_min_temperature = 0;
+    newy.y_max_temperature = 0;
+    newy.y_flocculation = 0;
+    newy.y_attenuation = 0;
+    newy.y_cells = 0;
+    newy.y_tolerance = 0;
+    newy.y_inventory = 0;
+    newy.y_use = 0;
+    newy.y_sta1 = false;
+    newy.y_bacteria = false;
+    newy.y_harvest_top = false;
+    newy.y_harvest_time = 0;
+    newy.y_pitch_temperature = 0;
+    newy.y_pofpos = false;
+    newy.y_zymocide = 0;
+    newy.y_gr_hl_lo = 0;
+    newy.y_sg_lo = 0;
+    newy.y_gr_hl_hi = 0;
+    newy.y_sg_hi = 0;
+    newy.y_cost = 0;
+
+    product->yeasts.append(newy);
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteYeastRow_clicked()
+{
+    if (product->locked || product->yeasts.size() < 1)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete yeast row" << row << product->yeasts.size();
+
+    int rc = QMessageBox::warning(this, tr("Delete yeast"), tr("Delete %1").arg(product->yeasts.at(row).y_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    product->yeasts.removeAt(row);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::yeast_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "yeast_amount_changed()" << product->yeasts_row << val;
+
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	product->yeasts[product->yeasts_row].y_amount = val;
+        item = new QTableWidgetItem(QString("%1 pack").arg(val, 1, 'f', 0, '0'));
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+	product->yeasts[product->yeasts_row].y_amount = val / 1000.0;
+        item = new QTableWidgetItem(QString("%1 gr").arg(val, 3, 'f', 2, '0'));
+    } else {
+        product->yeasts[product->yeasts_row].y_amount = val / 1000.0;
+        item = new QTableWidgetItem(QString("%1 ml").arg(val, 3, 'f', 2, '0'));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 9, item);
+
+    calcYeast();
+    is_changed();
+}
+
+
+void EditProduct::yeast_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = yinstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+        return;
+
+    qDebug() << "yeast_select_changed()" << product->yeasts_row << val << instock;
+
+    /*
+     * Search the yeast pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,laboratory,product_id,type,form,min_temperature,max_temperature,flocculation,attenuation,"
+	          "cells,tolerance,sta1,bacteria,harvest_top,harvest_time,pitch_temperature,pofpos,zymocide,"
+		  "gr_hl_lo,sg_lo,gr_hl_hi,sg_hi,cost FROM inventory_yeasts ";
+    if (instock)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY laboratory,product_id,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+        query.next();
+    }
+    qDebug() << "found" << query.value(0).toString() << query.value(2).toString();
+
+    /*
+     * Replace the yeast record contents
+     */
+    product->yeasts[product->yeasts_row].y_name = query.value(0).toString();
+    product->yeasts[product->yeasts_row].y_laboratory = query.value(1).toString();
+    product->yeasts[product->yeasts_row].y_product_id = query.value(2).toString();
+    product->yeasts[product->yeasts_row].y_type = query.value(3).toInt();
+    product->yeasts[product->yeasts_row].y_form = query.value(4).toInt();
+    product->yeasts[product->yeasts_row].y_min_temperature = query.value(5).toDouble();
+    product->yeasts[product->yeasts_row].y_max_temperature = query.value(6).toDouble();
+    product->yeasts[product->yeasts_row].y_flocculation = query.value(7).toInt();
+    product->yeasts[product->yeasts_row].y_attenuation = query.value(8).toDouble();
+    product->yeasts[product->yeasts_row].y_cells = query.value(9).toDouble();
+    product->yeasts[product->yeasts_row].y_tolerance = query.value(10).toDouble();
+    product->yeasts[product->yeasts_row].y_sta1 = query.value(11).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_bacteria = query.value(12).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_harvest_top = query.value(13).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_harvest_time = query.value(14).toInt();
+    product->yeasts[product->yeasts_row].y_pitch_temperature = query.value(15).toDouble();
+    product->yeasts[product->yeasts_row].y_pofpos = query.value(16).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_zymocide = query.value(17).toInt();
+    product->yeasts[product->yeasts_row].y_gr_hl_lo = query.value(18).toInt();
+    product->yeasts[product->yeasts_row].y_sg_lo = query.value(19).toDouble();
+    product->yeasts[product->yeasts_row].y_gr_hl_hi = query.value(20).toInt();
+    product->yeasts[product->yeasts_row].y_sg_hi = query.value(21).toDouble();
+    product->yeasts[product->yeasts_row].y_cost = query.value(22).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    ynameEdit->setText(product->yeasts.at(product->yeasts_row).y_name);
+    ylaboratoryEdit->setText(product->yeasts.at(product->yeasts_row).y_laboratory);
+    yproduct_idEdit->setText(product->yeasts.at(product->yeasts_row).y_product_id);
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	yamountEdit->setDecimals(0);
+	yamountEdit->setSingleStep(1.0);
+	yamountLabel->setText(tr("Total packs:"));
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+	yamountEdit->setDecimals(1);
+    	yamountEdit->setSingleStep(0.5);
+	yamountLabel->setText(tr("Amount in gr:"));
+    } else {
+	yamountEdit->setDecimals(1);
+	yamountEdit->setSingleStep(0.5);
+	yamountLabel->setText(tr("Amount in ml:"));
+    }
+
+    ui->yeastsTable->setItem(product->yeasts_row, 0, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_name));
+    ui->yeastsTable->setItem(product->yeasts_row, 1, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_laboratory));
+    ui->yeastsTable->setItem(product->yeasts_row, 2, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_product_id));
+
+    item = new QTableWidgetItem(yeast_forms[product->yeasts.at(product->yeasts_row).y_form]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 3, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_min_temperature, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 5, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_max_temperature, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 6, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_tolerance, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 7, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_attenuation, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 8, item);
+
+    calcYeast();
+    is_changed();
+}
+
+
+void EditProduct::yeast_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "yeast_instock_changed()" << product->yeasts_row << val;
+
+    this->yselectEdit->setCurrentIndex(-1);
+    this->yselectEdit->clear();
+    QString sql = "SELECT name,laboratory,product_id,inventory FROM inventory_yeasts ";
+    if (val)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY laboratory,product_id,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->yselectEdit->addItem("");      // Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->yselectEdit->addItem(query.value(1).toString()+" - "+query.value(2).toString()+" "+query.value(0).toString() + 
+                        QString(" (%1 gr)").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
+        query.next();
+    }
+}
+
+
+void EditProduct::yeast_useat_changed(int val)
+{
+    qDebug() << "yeast_useat_changed()" << product->yeasts_row << val;
+
+    product->yeasts[product->yeasts_row].y_use = val;
+    QTableWidgetItem *item = new QTableWidgetItem(yeast_use[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 5, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::editYeastRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->yeasts_row = pb->objectName().toInt();
+    qDebug() << "Edit yeast row" << product->yeasts_row;
+    Yeasts backup = product->yeasts.at(product->yeasts_row);
+
+    QDialog* dialog = new QDialog(this);
+    dialog->resize(738, 260);
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
+    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+    buttonBox->setGeometry(QRect(30, 210, 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("Yeast name:"));
+    nameLabel->setGeometry(QRect(10, 10, 141, 20));
+    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *laboratoryLabel = new QLabel(dialog);
+    laboratoryLabel->setObjectName(QString::fromUtf8("laboratoryLabel"));
+    laboratoryLabel->setText(tr("Laboratory:"));
+    laboratoryLabel->setGeometry(QRect(10, 40, 141, 20));
+    laboratoryLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *product_idLabel = new QLabel(dialog);
+    product_idLabel->setObjectName(QString::fromUtf8("product_idLabel"));
+    product_idLabel->setText(tr("Laboratory:"));
+    product_idLabel->setGeometry(QRect(10, 70, 141, 20));
+    product_idLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *selectLabel = new QLabel(dialog);
+    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
+    selectLabel->setText(tr("Select yeast:"));
+    selectLabel->setGeometry(QRect(10,100, 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,100, 121, 20));
+    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    yamountLabel = new QLabel(dialog);
+    yamountLabel->setObjectName(QString::fromUtf8("amountLabel"));
+    if (product->yeasts.at(product->yeasts_row).y_form == 0)
+	yamountLabel->setText(tr("Total packs:"));
+    else if (product->yeasts.at(product->yeasts_row).y_form == 1)
+	yamountLabel->setText(tr("Amount in gr:"));
+    else
+	yamountLabel->setText(tr("Amount in ml:"));
+    yamountLabel->setGeometry(QRect(10, 130, 141, 20));
+    yamountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *useatLabel = new QLabel(dialog);
+    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
+    useatLabel->setText(tr("Use at:"));
+    useatLabel->setGeometry(QRect(10, 160, 141, 20));
+    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+
+    ynameEdit = new QLineEdit(dialog);
+    ynameEdit->setObjectName(QString::fromUtf8("ynameEdit"));
+    ynameEdit->setText(product->yeasts.at(product->yeasts_row).y_name);
+    ynameEdit->setGeometry(QRect(160, 10, 511, 23));
+    ynameEdit->setReadOnly(true);
+    ylaboratoryEdit = new QLineEdit(dialog);
+    ylaboratoryEdit->setObjectName(QString::fromUtf8("ylaboratoryEdit"));
+    ylaboratoryEdit->setText(product->yeasts.at(product->yeasts_row).y_laboratory);
+    ylaboratoryEdit->setGeometry(QRect(160, 40, 511, 23));
+    ylaboratoryEdit->setReadOnly(true);
+    yproduct_idEdit = new QLineEdit(dialog);
+    yproduct_idEdit->setObjectName(QString::fromUtf8("yproduct_idEdit"));
+    yproduct_idEdit->setText(product->yeasts.at(product->yeasts_row).y_product_id);
+    yproduct_idEdit->setGeometry(QRect(160, 70, 511, 23));
+    yproduct_idEdit->setReadOnly(true);
+    yselectEdit = new QComboBox(dialog);
+    yselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
+    yselectEdit->setGeometry(QRect(160,100, 371, 23));
+    yinstockEdit = new QCheckBox(dialog);
+    yinstockEdit->setObjectName(QString::fromUtf8("yinstockEdit"));
+    yinstockEdit->setGeometry(QRect(655,100, 85, 21));
+    yinstockEdit->setChecked(true);
+    yamountEdit = new QDoubleSpinBox(dialog);
+    yamountEdit->setObjectName(QString::fromUtf8("yamountEdit"));
+    yamountEdit->setGeometry(QRect(160, 130, 121, 24));
+    yamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    yamountEdit->setAccelerated(true);
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	yamountEdit->setDecimals(0);
+        yamountEdit->setSingleStep(1.0);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount);
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+    	yamountEdit->setDecimals(1);
+    	yamountEdit->setSingleStep(0.5);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount * 1000.0);
+    } else {
+	yamountEdit->setDecimals(1);
+        yamountEdit->setSingleStep(0.5);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount * 1000.0);
+    }
+    yamountEdit->setMaximum(1000000000.0);
+    useatEdit = new QComboBox(dialog);
+    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
+    useatEdit->setGeometry(QRect(160, 160, 161, 23));
+    useatEdit->addItem(tr("Primary"));
+    useatEdit->addItem(tr("Secondary"));
+    useatEdit->addItem(tr("Tertiary"));
+    useatEdit->addItem(tr("Bottle"));
+    useatEdit->setCurrentIndex(product->yeasts.at(product->yeasts_row).y_use);
+
+    yeast_instock_changed(true);
+
+    connect(yselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_select_changed);
+    connect(yamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::yeast_amount_changed);
+    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_useat_changed);
+    connect(yinstockEdit, &QCheckBox::stateChanged, this, &EditProduct::yeast_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->yeasts[product->yeasts_row] = backup;
+    } else {
+
+    }
+
+    disconnect(yselectEdit, nullptr, nullptr, nullptr);
+    disconnect(yamountEdit, nullptr, nullptr, nullptr);
+    disconnect(useatEdit, nullptr, nullptr, nullptr);
+    disconnect(yinstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
+void EditProduct::adjustYeasts(double factor)
+{
+    double amount;
+
+    if (product->yeasts.size() == 0)
+	return;
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+	if (product->yeasts.at(i).y_form == 1) { // Only adjust dry yeast
+	    amount = product->yeasts.at(i).y_amount * factor;
+	    product->yeasts[i].y_amount = amount;
+	}
+    }
+}
+

mercurial