Added calcSupplies() which shows if ingredients are in stock to brew the recipe. In tab 3, changed the fermentables numbers into more readable enum constants. Disable delete and edit buttons in fermentables rows for bottle and kegs sugars. Better mash time calculation in calcFermentables. Added check supplies. Added recalculate volumes. Update inventory when a fermentable is replaced. Added tooltips in brewing salts fields in the miscs table.

Tue, 03 May 2022 20:05:04 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 03 May 2022 20:05:04 +0200
changeset 190
bb6c06910f0f
parent 189
722a4eed545d
child 191
7446ee2fb427

Added calcSupplies() which shows if ingredients are in stock to brew the recipe. In tab 3, changed the fermentables numbers into more readable enum constants. Disable delete and edit buttons in fermentables rows for bottle and kegs sugars. Better mash time calculation in calcFermentables. Added check supplies. Added recalculate volumes. Update inventory when a fermentable is replaced. Added tooltips in brewing salts fields in the miscs table.

src/EditProduct.cpp file | annotate | diff | comparison | revisions
src/EditProduct.h file | annotate | diff | comparison | revisions
src/EditProductTab1.cpp file | annotate | diff | comparison | revisions
src/EditProductTab3.cpp file | annotate | diff | comparison | revisions
src/EditProductTab5.cpp file | annotate | diff | comparison | revisions
src/global.h file | annotate | diff | comparison | revisions
ui/EditProduct.ui file | annotate | diff | comparison | revisions
--- a/src/EditProduct.cpp	Mon May 02 22:42:19 2022 +0200
+++ b/src/EditProduct.cpp	Tue May 03 20:05:04 2022 +0200
@@ -1092,6 +1092,23 @@
 }
 
 
+void EditProduct::calcSupplies()
+{
+    if (product->inventory_reduced > PROD_STAGE_PACKAGE) {
+	ui->ok_pmptLabel->setVisible(false);
+	ui->ok_pmptIcon->setVisible(false);
+	return;
+    }
+
+    qDebug() << "calcSupplies() f:" << product->fermentables_ok /*<< "h:" << product->hops_ok << "m:" << product->miscs_ok << "y:" << product->yeasts_ok << "w:" << product->waters_ok*/;
+    if (product->fermentables_ok /*&& product->hops_ok && product->miscs_ok && product->yeasts_ok && product->waters_ok */) {
+	ui->ok_pmptIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/silk/tick.png")));
+    } else {
+	ui->ok_pmptIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/silk/error.png")));
+    }
+}
+
+
 void EditProduct::refreshAll()
 {
     refreshFermentables();
--- a/src/EditProduct.h	Mon May 02 22:42:19 2022 +0200
+++ b/src/EditProduct.h	Tue May 03 20:05:04 2022 +0200
@@ -159,6 +159,7 @@
     static bool misc_sort_test(const Miscs &D1, const Miscs &D2);
     static bool yeast_sort_test(const Yeasts &D1, const Yeasts &D2);
     void WindowTitle();
+    void calcSupplies();
     void showEquipment();
     void initEquipment();
     void setStage();
--- a/src/EditProductTab1.cpp	Mon May 02 22:42:19 2022 +0200
+++ b/src/EditProductTab1.cpp	Tue May 03 20:05:04 2022 +0200
@@ -298,7 +298,7 @@
     ui->tabWidget->setTabEnabled(8, stage > PROD_STAGE_PLAN);
 
     /* Tab 10, fermentation */
-    ui->tabWidget->setTabEnabled(9, stage > PROD_STAGE_PLAN);
+    ui->tabWidget->setTabEnabled(9, stage > PROD_STAGE_WAIT);
 
 
     /* Tab 11, packaging */
@@ -306,7 +306,7 @@
 
 
     /* Tab 12, tasting */
-    ui->tabWidget->setTabEnabled(11, stage > PROD_STAGE_PLAN);
+    ui->tabWidget->setTabEnabled(11, stage > PROD_STAGE_PACKAGE);
 
 }
 
--- a/src/EditProductTab3.cpp	Mon May 02 22:42:19 2022 +0200
+++ b/src/EditProductTab3.cpp	Tue May 03 20:05:04 2022 +0200
@@ -109,9 +109,11 @@
 
 	item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(i).f_inventory, 4, 'f', 3, '0'));
         item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+	if (product->fermentables.at(i).f_inventory < product->fermentables.at(i).f_amount)
+	    item->setForeground(QBrush(QColor(Qt::red)));
         ui->fermentablesTable->setItem(i, 8, item);
 
-	if (product->fermentables.at(i).f_added < 4) {
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
             item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).f_percentage, 2, 'f', 1, '0'));
 	} else {
 	    item = new QTableWidgetItem(QString(""));	// Blank for bottling and kegging.
@@ -122,27 +124,36 @@
 	to100Fermentables(i);
 
 	/* Add the Delete row button */
-        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(deleteFermentRow_clicked()));
-        pLayout = new QHBoxLayout(pWidget);
-        pLayout->addWidget(btn_dele);
-        pLayout->setContentsMargins(5, 0, 5, 0);
-        pWidget->setLayout(pLayout);
-        ui->fermentablesTable->setCellWidget(i, 11, pWidget);
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
+            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(deleteFermentRow_clicked()));
+            pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_dele);
+            pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->fermentablesTable->setCellWidget(i, 11, 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(editFermentRow_clicked()));
-	pLayout = new QHBoxLayout(pWidget);
-        pLayout->addWidget(btn_edit);
-	pLayout->setContentsMargins(5, 0, 5, 0);
-        pWidget->setLayout(pLayout);
-        ui->fermentablesTable->setCellWidget(i, 12, 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(editFermentRow_clicked()));
+	    pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_edit);
+	    pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->fermentablesTable->setCellWidget(i, 12, pWidget);
+	} else {
+	    item = new QTableWidgetItem("");
+	    item->setToolTip(tr("Edit this from the package tab"));
+            ui->fermentablesTable->setItem(i, 11, item);
+	    item = new QTableWidgetItem("");
+            item->setToolTip(tr("Edit this from the package tab"));
+            ui->fermentablesTable->setItem(i, 12, item);
+	}
     }
 }
 
@@ -150,7 +161,7 @@
 void EditProduct::calcFermentables()
 {
     int		i;
-    double	psugar = 0, pcara = 0, d, s = 0, x, color;
+    double	psugar = 0, pcara = 0, d, s = 0, x, cw, color;
     double	vol = 0;		// Volume sugars after boil
     double	addedS = 0;		// Added sugars after boil
     double	addedmass = 0;		// Added mass after boil
@@ -165,6 +176,8 @@
     double	colort = 0;		// Colors srm * vol totals
     double	colorh = 0;		// Colors ebc * vol * kt
     double	colorn = 0;		// Colors ebc * pt * pct
+    double	bv = 0.925;		// Beer loss efficiency
+    double	sr = 0.95;		// Mash and sparge efficiency
 
     qDebug() << "calcFermentables()";
 
@@ -177,10 +190,14 @@
 	    if (product->mashs.at(i).step_type == 0)		// Infusion
 		mashinfuse += product->mashs.at(i).step_infuse_amount;
 	    if (product->mashs.at(i).step_temp < 75) {		// Ignore mashout
-		mashtime += product->mashs.at(i).step_time;
-		mashtemp += product->mashs.at(i).step_time * product->mashs.at(i).step_temp;
+		double timem = product->mashs.at(i).step_time;
+		if (i > 0)
+		    timem += product->mashs.at(i).ramp_time;
+		mashtime += timem;
+		mashtemp += timem * product->mashs.at(i).step_temp;
 	    }
 	}
+	mashtime += 5;	// Correction for missing last ramp_time.
 	mashtemp = mashtemp / mashtime;
 	mvol = mashinfuse;
 	qDebug() << "  mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
@@ -223,15 +240,16 @@
     }
     qDebug() << "  adjust to 100" << product->fermentables_use100;
 
+    product->fermentables_ok = true;
     product->mashs_kg = 0;
     for (i = 0; i < product->fermentables.size(); i++) {
-	if (product->fermentables.at(i).f_type == 1 && product->fermentables.at(i).f_added < 4)		// Sugars
+	if (product->fermentables.at(i).f_type == FERMENTABLE_TYPE_SUGAR && product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
 	    psugar += product->fermentables.at(i).f_percentage;
-	if (product->fermentables.at(i).f_graintype == 2 && product->fermentables.at(i).f_added < 4)	// Crystal/Cara
+	if (product->fermentables.at(i).f_graintype == FERMENTABLE_GRAINTYPE_CRYSTAL && product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
 	    pcara += product->fermentables.at(i).f_percentage;
 	d = product->fermentables.at(i).f_amount * (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
-	if (product->fermentables.at(i).f_added == 0) {					// Mash
-	    if (mvol > 0) {							// If mash volume is known
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH) {
+	    if (mvol > 0) {	// If mash volume is known
 		mvol += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_moisture / 100;
 		s += d;
 	    }
@@ -239,32 +257,42 @@
 	    sugarsm += d;
 	    product->mashs_kg += product->fermentables.at(i).f_amount;
 	}
-	if (product->fermentables.at(i).f_added == 0 || product->fermentables.at(i).f_added == 1)		// Mash or boil
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH || product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOIL)
 	    sugarsf += d;
-	if (product->fermentables.at(i).f_added == 2 || product->fermentables.at(i).f_added == 3) {		// Fermentation or lagering
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_FERMENTATION || product->fermentables.at(i).f_added == FERMENTABLE_ADDED_LAGERING) {
 	    x = (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
 	    addedS += product->fermentables.at(i).f_amount * x;
 	    addedmass += product->fermentables.at(i).f_amount;
 	    vol += (x * sugardensity + (1 - x) * 1) * product->fermentables.at(i).f_amount;
 	}
-	if (product->fermentables.at(i).f_added == 0 &&
-	    (product->fermentables.at(i).f_type == 0 || product->fermentables.at(i).f_type == 4) &&
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH &&
+	    (product->fermentables.at(i).f_type == FERMENTABLE_TYPE_GRAIN || product->fermentables.at(i).f_type == FERMENTABLE_TYPE_ADJUCT) &&
 	    product->fermentables.at(i).f_color < 50) {
 	    lintner += product->fermentables.at(i).f_diastatic_power * product->fermentables.at(i).f_amount;
 	}
-	if (product->fermentables.at(i).f_added < 4) {
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
 	    colort += product->fermentables.at(i).f_amount * Utils::ebc_to_srm(product->fermentables.at(i).f_color);
 	    colorh += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_color * Utils::get_kt(product->fermentables.at(i).f_color);
 	    colorn += (product->fermentables.at(i).f_percentage / 100) * product->fermentables.at(i).f_color;	// For 8.6 Pt wort.
 	}
-	if (product->fermentables.at(i).f_added == 4) {	// Bottle priming
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOTTLE) {	// Bottle priming
 	    ui->bottle_sug_weightShow->setValue(product->fermentables.at(i).f_amount * 1000);
 	    // product->fermentables.at(i).f_name  select in dropdown
 	}
-	if (product->fermentables.at(i).f_added == 5) {	// Keg priming
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_KEGS) {	// Keg priming
 
 	}
+	/* Check supplies */
+	if ((((product->inventory_reduced <= PROD_STAGE_BREW)     && (product->fermentables.at(i).f_added <= FERMENTABLE_ADDED_BOIL)) ||
+	     ((product->inventory_reduced <= PROD_STAGE_PRIMARY)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_FERMENTATION)) ||
+	     ((product->inventory_reduced <= PROD_STAGE_TERTIARY) && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_LAGERING)) ||
+	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOTTLE)) ||
+	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_KEGS))) &&
+	       product->fermentables.at(i).f_inventory < product->fermentables.at(i).f_amount) {
+	    product->fermentables_ok = false;
+	}
     }
+    qDebug() << "  supplies" << product->fermentables_ok;
     qDebug() << "  colort" << colort << "colorh" << colorh << "colorn" << colorn;
     qDebug() << "  psugar" << psugar << "pcara" << pcara << "mvol" << mvol;
     qDebug() << "  sugarsf" << sugarsf << "sugarsm" << sugarsm;
@@ -274,32 +302,78 @@
     product->est_mash_sg = Utils::plato_to_sg(s);
     ui->brew_mashsgShow->setValue(product->est_mash_sg);
 
-    double og = Utils::estimate_sg(sugarsf + addedS, product->batch_size);
-    qDebug() << "  OG" << ui->est_ogEdit->value() << og;
-    product->est_og = og;
-    ui->est_ogEdit->setValue(og);
-    ui->est_og2Edit->setValue(og);
-    ui->est_og4Edit->setValue(og);
-    ui->est_ogShow->setValue(og);
+    /* Estimate total recipe OG */
+    product->est_og = Utils::estimate_sg(sugarsf + addedS, product->batch_size);
+    qDebug() << "  OG" << ui->est_ogEdit->value() << product->est_og;
+    ui->est_ogEdit->setValue(product->est_og);
+    ui->est_og2Edit->setValue(product->est_og);
+    ui->est_og4Edit->setValue(product->est_og);
+    ui->est_ogShow->setValue(product->est_og);
 
+    /* Estimate SG in kettle after boil */
     product->est_og3 = Utils::estimate_sg(sugarsf, product->batch_size);
     ui->brew_aboilsgShow->setValue(product->est_og3);
 
+    /* Estimate SG in kettle before boil */
     product->preboil_sg = Utils::estimate_sg(sugarsm, product->boil_size);
     ui->brew_preboilsgShow->setValue(product->preboil_sg);
     qDebug() << "  preboil SG" << product->preboil_sg;
 
     /*
+     * Recalculate volumes
+     */
+    double aboil_volume = product->batch_size;
+    if (product->brew_aboil_volume > 0)
+	aboil_volume = product->brew_aboil_volume / 1.04;	// Volume @ 20 degrees.
+    if (product->brew_fermenter_tcloss == 0) {
+	product->brew_fermenter_tcloss = product->eq_trub_chiller_loss;
+	ui->brew_trublossEdit->setValue(product->brew_fermenter_tcloss);
+    }
+    product->brew_fermenter_volume = aboil_volume - product->brew_fermenter_tcloss + product->brew_fermenter_extrawater;
+    ui->brew_tofermentEdit->setValue(product->brew_fermenter_volume);
+    /* Calculate SG in fermenter */
+    double ogx = product->brew_aboil_sg;
+    if (ogx < 1.002)
+	ogx = product->est_og3;
+    double topw = product->brew_fermenter_extrawater;
+
+    if (product->brew_fermenter_volume > 0) {
+	double sug = Utils::sg_to_plato(ogx) * product->brew_fermenter_volume * ogx / 100;  //kg of sugar in
+   	sug += addedS; //kg
+
+	if ((product->brew_fermenter_volume * ogx + addedmass) > 0) {
+	    double pt = 100 * sug / (product->brew_fermenter_volume * ogx + addedmass + topw);
+	    product->og = product->brew_fermenter_sg = round(Utils::plato_to_sg(pt) * 10000) / 10000.0;
+	    ui->brew_fermentsgShow->setValue(product->brew_fermenter_sg);
+	    // color
+	    if (product->color_method == 4) {		// Naudts
+		product->brew_fermenter_color = round(((pt / 8.6) * colorn) + (product->boil_time / 60));
+	    } else if (product->color_method == 3) {	// Hans Halberstadt
+		product->brew_fermenter_color = round((4.46 * bv * sr) / (aboil_volume + topw) * colorh);
+	    } else {
+		cw = colort / (aboil_volume + topw) * 8.34436;
+		product->brew_fermenter_color = Utils::kw_to_ebc(product->color_method, cw);
+	    }
+	    ui->brew_fermentcolorShow->setValue(product->brew_fermenter_color);
+	    ui->brew_fermentcolorShow->setStyleSheet(Utils::ebc_to_style(product->brew_fermenter_color));
+	}
+    } else {
+	// Negative volume
+	product->brew_fermenter_sg = product->brew_fermenter_color = 0;
+	ui->brew_fermentsgShow->setValue(0);
+	ui->brew_fermentcolorShow->setStyleSheet(0);
+	ui->brew_fermentcolorShow->setValue(0);
+    }
+
+    /*
      * Color of the wort
      */
     if (product->color_method == 4) {				// Naudts
-	color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (product->boil_time / 60));
+	color = round(((Utils::sg_to_plato(product->est_og) / 8.6) * colorn) + (product->boil_time / 60));
     } else if (product->color_method == 3) {			// Hans Halberstadt
-	double bv = 0.925;					// Beer loss efficiency
-	double sr = 0.95;					// Mash and sparge efficiency
 	color = round((4.46 * bv * sr) / product->batch_size * colorh);
     } else {
-	double cw = colort / product->batch_size * 8.34436;
+	cw = colort / product->batch_size * 8.34436;
 	color = Utils::kw_to_ebc(product->color_method, cw);
 	//qDebug() << "  oud EBC" << color << "new EBC" << Utils::kw_to_newebc(product->color_method, cw) << "SRM" << Utils::kw_to_srm(product->color_method, cw);
     }
@@ -327,50 +401,68 @@
      * Calculate the apparant attenuation.
      */
     double svg = 0;
+    double initcells = 0;
     if (product->yeasts.size() > 0) {
         for (i = 0; i < product->yeasts.size(); i++) {
 	    if (product->yeasts.at(i).y_use == 0) {		// Used in primary
 		if (product->yeasts.at(i).y_attenuation > svg)
 		    svg = product->yeasts.at(i).y_attenuation;	// Take the highest if multiple yeasts.
 	    }
+	    if (product->yeasts.at(i).y_form == 0)
+		initcells += (product->yeasts.at(i).y_cells / 1000000000) * product->yeasts.at(i).y_amount * (product->starter_viability / 100);
+	    else
+		initcells += (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * (product->starter_viability / 100);
 	    // TODO: brett or others in secondary.
+	    product->yeasts_ok = true;
+	    if ((((product->inventory_reduced <= PROD_STAGE_PRIMARY)   && (product->yeasts.at(i).y_use == 0)) ||  // Primary
+        	 ((product->inventory_reduced <= PROD_STAGE_SECONDARY) && (product->yeasts.at(i).y_use == 1)) ||  // Secondary
+        	 ((product->inventory_reduced <= PROD_STAGE_TERTIARY)  && (product->yeasts.at(i).y_use == 2)) ||  // Tertiary
+        	 ((product->inventory_reduced <= PROD_STAGE_PACKAGE)   && (product->yeasts.at(i).y_use == 3))) && // Bottle
+        	  (product->yeasts.at(i).y_inventory < product->yeasts.at(i).y_amount)) {
+		product->yeasts_ok = false;
+	    }
 	}
 	qDebug() << "  SVG" << svg;
     }
+    calcSupplies();
     if (svg == 0)
 	svg = 77.0;
     ui->est_svgEdit->setValue(svg);
 
-    double fg;
     if (product->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
-	fg = Utils::estimate_fg(psugar, pcara, mashinfuse / product->mashs_kg, mashtime, mashtemp, svg, og);
+	product->est_fg = Utils::estimate_fg(psugar, pcara, mashinfuse / product->mashs_kg, mashtime, mashtemp, svg, product->est_og);
     else
-	fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
-    qDebug() << "  FG" << ui->est_fgEdit->value() << fg;
-    product->est_fg = fg;
-    ui->est_fgEdit->setValue(fg);
-    ui->est_fg3Edit->setValue(fg);
-    ui->est_fgShow->setValue(fg);
+	product->est_fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, product->est_og);
+    qDebug() << "  FG" << ui->est_fgEdit->value() << product->est_fg;
+    ui->est_fgEdit->setValue(product->est_fg);
+    ui->est_fg3Edit->setValue(product->est_fg);
+    ui->est_fgShow->setValue(product->est_fg);
 
-    double abv = Utils::abvol(og, fg);
-    qDebug() << "  ABV" << ui->est_abvEdit->value() << abv;
-    ui->est_abvEdit->setValue(abv);
-    ui->est_abv2Edit->setValue(abv);
-    ui->est_abvShow->setValue(abv);
-    product->est_abv = abv;
+    /*
+     * Calculate the final values if available.
+     */
+    if ((product->stage >= 6) && (product->fg > 0.990) && (product->fg < product->brew_fermenter_sg)) {
+
+    }
+
+    product->est_abv = Utils::abvol(product->est_og, product->est_fg);
+    qDebug() << "  ABV" << ui->est_abvEdit->value() << product->est_abv;
+    ui->est_abvEdit->setValue(product->est_abv);
+    ui->est_abv2Edit->setValue(product->est_abv);
+    ui->est_abvShow->setValue(product->est_abv);
 
     /*
      * Calculate kilocalories/liter. Formula from brouwhulp.
      * Take the alcohol and sugar parts and then combine.
      */
-    double alc = 1881.22 * fg * (og - fg) / (1.775 - og);
-    double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004);
+    double alc = 1881.22 * product->est_fg * (product->est_og - product->est_fg) / (1.775 - product->est_og);
+    double sug = 3550 * product->est_fg * (0.1808 * product->est_og + 0.8192 * product->est_fg - 1.0004);
     ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296)));
 
     // Bottle priming
     double priming_total = 0;
     for (i = 0; i < product->fermentables.size(); i++) {
-	if (product->fermentables.at(i).f_added == 4) {
+	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOTTLE) {
 	    priming_total += ((product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100)) *
 		    		product->fermentables.at(i).f_amount;
 	    qDebug() << "  priming" << product->fermentables.at(i).f_amount << "total" << priming_total;
@@ -400,11 +492,11 @@
     double sug = Utils::sg_to_plato(og) * product->batch_size * og / 100.0;	// total amount of sugars in kg.
 
     for (i = 0; i < product->fermentables.size(); i++) {
-	if (product->fermentables.at(i).f_added < 4) {
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
 	    d = product->fermentables.at(i).f_percentage / 100.0 *
 		    (product->fermentables.at(i).f_yield / 100.0) *
 		    (1 - product->fermentables.at(i).f_moisture / 100.0);
-	    if (product->fermentables.at(i).f_added == 0)  // Mash
+	    if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH)
 		d = efficiency / 100.0 * d;
 	    tot += d;
 	}
@@ -477,7 +569,7 @@
     newf.f_supplier = "";
     newf.f_amount = 0;
     newf.f_cost = 0;
-    newf.f_type = 0;
+    newf.f_type = FERMENTABLE_TYPE_GRAIN;
     newf.f_yield = 0;
     newf.f_color = 0;
     newf.f_coarse_fine_diff = 0;
@@ -486,8 +578,8 @@
     newf.f_protein = 0;
     newf.f_dissolved_protein = 0;
     newf.f_max_in_batch = 100;
-    newf.f_graintype = 0;
-    newf.f_added = 0;
+    newf.f_graintype = FERMENTABLE_GRAINTYPE_BASE;
+    newf.f_added = FERMENTABLE_ADDED_MASH;
     newf.f_recommend_mash = true;
     newf.f_add_after_boil = false;
     newf.f_adjust_to_total_100 = false;
@@ -524,10 +616,10 @@
      */
     double total = 0;
     for (int i = 0; i < product->fermentables.size(); i++)
-        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
             total += product->fermentables.at(i).f_amount;
     for (int i = 0; i < product->fermentables.size(); i++)
-        if (product->fermentables.at(i).f_added < 4)
+        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
             product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
 
     is_changed();
@@ -540,7 +632,7 @@
     QTableWidgetItem *item;
     double	total = 0, perc;
 
-    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4)
+    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE)
 	return;
 
     qDebug() << "ferment_amount_changed()" << product->fermentables_row << val;
@@ -551,13 +643,13 @@
     ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
 
     for (int i = 0; i < product->fermentables.size(); i++)
-	if (product->fermentables.at(i).f_added < 4)		// Only before bottle/kegging
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
 	    total += product->fermentables.at(i).f_amount;
     /*
      * Recalculate the percentages
      */
     for (int i = 0; i < product->fermentables.size(); i++) {
-	if (product->fermentables.at(i).f_added < 4) {
+	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
 	    perc = product->fermentables.at(i).f_amount / total * 100;
 	    product->fermentables[i].f_percentage = perc;
 	    item = new QTableWidgetItem(QString("%1%").arg(perc, 2, 'f', 1, '0'));
@@ -585,7 +677,7 @@
      * this is not the entry to be adjusted to 100.
      */
     for (int i = 0; i < product->fermentables.size(); i++) {
-        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
             total += product->fermentables.at(i).f_amount;
 	if (product->fermentables.at(i).f_adjust_to_total_100)
 	    row100 = i;
@@ -625,7 +717,7 @@
 {
     qDebug() << "ferment_to100_changed()" << product->fermentables_row << val << product->fermentables_use100;
 
-    if (product->fermentables.at(product->fermentables_row).f_added >= 4) {
+    if (product->fermentables.at(product->fermentables_row).f_added >= FERMENTABLE_ADDED_BOTTLE) {
 	const QSignalBlocker blocker1(to100Edit);
 	product->fermentables[product->fermentables_row].f_adjust_to_total_100 = false;
 	to100Edit->setChecked(false);
@@ -684,7 +776,7 @@
      * Search the fermentable pointed by the index and instock flag.
      */
     QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch,"
-	  	  "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables ";
+	  	  "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57,inventory FROM inventory_fermentables ";
     if (instock)
 	sql.append("WHERE inventory > 0 ");
     sql.append("ORDER BY supplier,name");
@@ -694,7 +786,6 @@
     for (int i = 0; i < (val - 1); i++) {
 	query.next();
     }
-    qDebug() << "found" << query.value(2).toString() << query.value(0).toString();
 
     /*
      * Replace the fermentable record contents
@@ -717,6 +808,7 @@
     product->fermentables[product->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false;
     product->fermentables[product->fermentables_row].f_di_ph = query.value(16).toDouble();
     product->fermentables[product->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble();
+    product->fermentables[product->fermentables_row].f_inventory = query.value(18).toDouble();
 
     /*
      * Update the visible fields
@@ -745,6 +837,12 @@
     item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
     ui->fermentablesTable->setItem(product->fermentables_row, 6, item);
 
+    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(product->fermentables_row).f_inventory, 4, 'f', 3, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    if (product->fermentables.at(product->fermentables_row).f_inventory < product->fermentables.at(product->fermentables_row).f_amount)
+	item->setForeground(QBrush(QColor(Qt::red)));
+    ui->fermentablesTable->setItem(product->fermentables_row, 8, item);
+
     calcFermentables();
     is_changed();
 }
@@ -783,15 +881,15 @@
     item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
     ui->fermentablesTable->setItem(product->fermentables_row, 5, item);
 
-    famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
-    pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4));
+    famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE);
+    pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE));
 
     double total = 0;
     for (int i = 0; i < product->fermentables.size(); i++)
-        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
             total += product->fermentables.at(i).f_amount;
     for (int i = 0; i < product->fermentables.size(); i++)
-        if (product->fermentables.at(i).f_added < 4)
+        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
             product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
 
     is_changed();
@@ -897,7 +995,7 @@
     pctEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
     pctEdit->setAccelerated(true);
     pctEdit->setDecimals(1);
-    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4) {
+    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE) {
     	if (product->fermentables.at(product->fermentables_row).f_adjust_to_total_100)
 	    pctEdit->setReadOnly(true);
     	else
@@ -916,8 +1014,6 @@
     faddedEdit->addItem(tr("Boil"));
     faddedEdit->addItem(tr("Fermentation"));
     faddedEdit->addItem(tr("Lagering"));
-    faddedEdit->addItem(tr("Bottle"));
-    faddedEdit->addItem(tr("Kegs"));
     faddedEdit->setCurrentIndex(product->fermentables.at(product->fermentables_row).f_added);
 
     to100Edit = new QCheckBox(dialog);
@@ -960,13 +1056,13 @@
 	 */
 	double total = 0;
 	for (int i = 0; i < product->fermentables.size(); i++)
-	    if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+	    if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
 		total += product->fermentables.at(i).f_amount;
 	product->fermentables_use100 = false;
 	for (int i = 0; i < product->fermentables.size(); i++) {
 	    if (product->fermentables.at(i).f_adjust_to_total_100)
 	    	product->fermentables_use100 = true;
-	    if (product->fermentables.at(i).f_added < 4) {
+	    if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
 		product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
 	    }
 	}
--- a/src/EditProductTab5.cpp	Mon May 02 22:42:19 2022 +0200
+++ b/src/EditProductTab5.cpp	Tue May 03 20:05:04 2022 +0200
@@ -108,8 +108,12 @@
 	 * Not for water agents, these are set on the water tab.
 	 */
 	if (product->miscs.at(i).m_type == 4) {
-	    ui->miscsTable->removeCellWidget(i, 6);
-	    ui->miscsTable->removeCellWidget(i, 7);
+	    item = new QTableWidgetItem("");
+            item->setToolTip(tr("Edit this from the water tab"));
+            ui->miscsTable->setItem(i, 6, item);
+            item = new QTableWidgetItem("");
+            item->setToolTip(tr("Edit this from the water tab"));
+            ui->miscsTable->setItem(i, 7, item);
 	} else {
             pWidget = new QWidget();
             QPushButton* btn_dele = new QPushButton();
--- a/src/global.h	Mon May 02 22:42:19 2022 +0200
+++ b/src/global.h	Tue May 03 20:05:04 2022 +0200
@@ -524,9 +524,14 @@
      */
     int		fermentables_row;	///< Current row, -1 is invalid.
     bool	fermentables_use100;	///< Use percentages instead of amount
+    bool	fermentables_ok;	///< Inventory check
     int		hops_row;
+    bool	hops_ok;
     int		miscs_row;
+    bool	miscs_ok;
     int		yeasts_row;
+    bool	yeasts_ok;
+    bool	waters_ok;
     int		mashs_row;
     double	mashs_kg;		///< Kg fermentables in the mash.
     int		mashs_time;		///< Total mash time.
@@ -572,8 +577,38 @@
 extern const QStringList prod_split;
 extern const QStringList recipe_types;
 extern const QStringList style_types;
+
+enum FermentableTypes {
+	FERMENTABLE_TYPE_GRAIN,
+	FERMENTABLE_TYPE_SUGAR,
+	FERMENTABLE_TYPE_EXTRACT,
+	FERMENTABLE_TYPE_DRY_EXTRACT,
+	FERMENTABLE_TYPE_ADJUCT
+};
+
 extern const QStringList fermentable_types;
+
+enum FermentableGraintypes {
+	FERMENTABLE_GRAINTYPE_BASE,
+	FERMENTABLE_GRAINTYPE_ROAST,
+	FERMENTABLE_GRAINTYPE_CRYSTAL,
+	FERMENTABLE_GRAINTYPE_KILNED,
+	FERMENTABLE_GRAINTYPE_SOUR_MALT,
+	FERMENTABLE_GRAINTYPE_SPECIAL,
+	FERMENTABLE_GRAINTYPE_NO_MALT
+};
+
 extern const QStringList fermentable_graintypes;
+
+enum FermentableAdded {
+	FERMENTABLE_ADDED_MASH,
+	FERMENTABLE_ADDED_BOIL,
+	FERMENTABLE_ADDED_FERMENTATION,
+	FERMENTABLE_ADDED_LAGERING,
+	FERMENTABLE_ADDED_BOTTLE,
+	FERMENTABLE_ADDED_KEGS
+};
+
 extern const QStringList fermentable_added;
 extern const QStringList hop_types;
 extern const QStringList hop_forms;
--- a/ui/EditProduct.ui	Mon May 02 22:42:19 2022 +0200
+++ b/ui/EditProduct.ui	Tue May 03 20:05:04 2022 +0200
@@ -173,7 +173,7 @@
         <property name="geometry">
          <rect>
           <x>870</x>
-          <y>180</y>
+          <y>200</y>
           <width>131</width>
           <height>20</height>
          </rect>
@@ -189,7 +189,7 @@
         <property name="geometry">
          <rect>
           <x>1010</x>
-          <y>180</y>
+          <y>200</y>
           <width>61</width>
           <height>21</height>
          </rect>
@@ -1222,10 +1222,10 @@
          <bool>true</bool>
         </property>
        </widget>
-       <widget class="QLabel" name="completeLabel">
-        <property name="geometry">
-         <rect>
-          <x>620</x>
+       <widget class="QLabel" name="ok_pmptLabel">
+        <property name="geometry">
+         <rect>
+          <x>740</x>
           <y>180</y>
           <width>131</width>
           <height>20</height>
@@ -1238,6 +1238,22 @@
          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
         </property>
        </widget>
+       <widget class="QLabel" name="ok_pmptIcon">
+        <property name="geometry">
+         <rect>
+          <x>880</x>
+          <y>180</y>
+          <width>20</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="pixmap">
+         <pixmap resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">:/icons/silk/error.png</pixmap>
+        </property>
+       </widget>
       </widget>
       <widget class="QWidget" name="equipment">
        <attribute name="icon">

mercurial