src/EditProductTab3.cpp

changeset 175
f1ed3a2a94e9
child 177
62b8d701cd88
equal deleted inserted replaced
174:ceb8aa4ebd25 175:f1ed3a2a94e9
1 /**
2 * EditProduct.cpp is part of bmsapp.
3 *
4 * Tab 3, fermentables
5 *
6 * bmsapp is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * bmsapp is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21
22 bool EditProduct::ferment_sort_test(const Fermentables &D1, const Fermentables &D2)
23 {
24 if (D1.f_added > D2.f_added)
25 return false;
26 if (D1.f_added < D2.f_added)
27 return true;
28 return (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color);
29 }
30
31
32 void EditProduct::to100Fermentables(int row)
33 {
34 if (product->fermentables.at(row).f_adjust_to_total_100) {
35 QWidget *pWidget = new QWidget();
36 QLabel *label = new QLabel;
37 label->setPixmap(QPixmap(":icons/silk/tick.png"));
38 QHBoxLayout *pLayout = new QHBoxLayout(pWidget);
39 pLayout->addWidget(label);
40 pLayout->setAlignment(Qt::AlignCenter);
41 pLayout->setContentsMargins(0, 0, 0, 0);
42 pWidget->setLayout(pLayout);
43 ui->fermentablesTable->setCellWidget(row, 9, pWidget);
44 } else {
45 ui->fermentablesTable->removeCellWidget(row, 9);
46 }
47 }
48
49
50 void EditProduct::refreshFermentables()
51 {
52 QString w;
53 QWidget* pWidget;
54 QHBoxLayout* pLayout;
55 QTableWidgetItem *item;
56
57 qDebug() << "refreshFermentables" << product->fermentables.size();
58 std::sort(product->fermentables.begin(), product->fermentables.end(), ferment_sort_test);
59
60 const QStringList labels({tr("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"),
61 tr("Amount"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit") });
62 ui->fermentablesTable->setColumnCount(12);
63 ui->fermentablesTable->setColumnWidth(0, 150); /* Supplier */
64 ui->fermentablesTable->setColumnWidth(1, 225); /* Fermentable */
65 ui->fermentablesTable->setColumnWidth(2, 50); /* Color */
66 ui->fermentablesTable->setColumnWidth(3, 75); /* Type */
67 ui->fermentablesTable->setColumnWidth(4, 75); /* Graintype */
68 ui->fermentablesTable->setColumnWidth(5, 82); /* Added */
69 ui->fermentablesTable->setColumnWidth(6, 60); /* Yield */
70 ui->fermentablesTable->setColumnWidth(7, 90); /* Amount */
71 ui->fermentablesTable->setColumnWidth(8, 60); /* Procent */
72 ui->fermentablesTable->setColumnWidth(9, 50); /* 100% */
73 ui->fermentablesTable->setColumnWidth(10, 80); /* Delete */
74 ui->fermentablesTable->setColumnWidth(11, 80); /* Edit */
75 ui->fermentablesTable->setHorizontalHeaderLabels(labels);
76 ui->fermentablesTable->verticalHeader()->hide();
77 ui->fermentablesTable->setRowCount(product->fermentables.size());
78
79 for (int i = 0; i < product->fermentables.size(); i++) {
80
81 ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(product->fermentables.at(i).f_supplier));
82 ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(product->fermentables.at(i).f_name));
83
84 w = QString("%1").arg(product->fermentables.at(i).f_color, 1, 'f', 0, '0');
85 item = new QTableWidgetItem(w);
86 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
87 ui->fermentablesTable->setItem(i, 2, item);
88
89 item = new QTableWidgetItem(fermentable_types[product->fermentables.at(i).f_type]);
90 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
91 ui->fermentablesTable->setItem(i, 3, item);
92
93 item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(i).f_graintype]);
94 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
95 ui->fermentablesTable->setItem(i, 4, item);
96
97 item = new QTableWidgetItem(fermentable_added[product->fermentables.at(i).f_added]);
98 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
99 ui->fermentablesTable->setItem(i, 5, item);
100
101 item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).f_yield, 2, 'f', 1, '0'));
102 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
103 ui->fermentablesTable->setItem(i, 6, item);
104
105 item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(i).f_amount, 4, 'f', 3, '0'));
106 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
107 ui->fermentablesTable->setItem(i, 7, item);
108
109 if (product->fermentables.at(i).f_added < 4) {
110 item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).f_percentage, 2, 'f', 1, '0'));
111 } else {
112 item = new QTableWidgetItem(QString("")); // Blank for bottling and kegging.
113 }
114 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
115 ui->fermentablesTable->setItem(i, 8, item);
116
117 to100Fermentables(i);
118
119 /* Add the Delete row button */
120 pWidget = new QWidget();
121 QPushButton* btn_dele = new QPushButton();
122 btn_dele->setObjectName(QString("%1").arg(i)); /* Send row with the button */
123 btn_dele->setText(tr("Delete"));
124 connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteFermentRow_clicked()));
125 pLayout = new QHBoxLayout(pWidget);
126 pLayout->addWidget(btn_dele);
127 pLayout->setContentsMargins(5, 0, 5, 0);
128 pWidget->setLayout(pLayout);
129 ui->fermentablesTable->setCellWidget(i, 10, pWidget);
130
131 pWidget = new QWidget();
132 QPushButton* btn_edit = new QPushButton();
133 btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */
134 btn_edit->setText(tr("Edit"));
135 connect(btn_edit, SIGNAL(clicked()), this, SLOT(editFermentRow_clicked()));
136 pLayout = new QHBoxLayout(pWidget);
137 pLayout->addWidget(btn_edit);
138 pLayout->setContentsMargins(5, 0, 5, 0);
139 pWidget->setLayout(pLayout);
140 ui->fermentablesTable->setCellWidget(i, 11, pWidget);
141 }
142 }
143
144
145 void EditProduct::calcFermentables()
146 {
147 int i;
148 double psugar = 0, pcara = 0, d, s = 0, x, color;
149 double vol = 0; // Volume sugars after boil
150 double addedS = 0; // Added sugars after boil
151 double addedmass = 0; // Added mass after boil
152 double mvol = 0; // Mash volume
153 double lintner = 0; // Total product lintner
154 double sugarsf = 0; // fermentable sugars mash + boil
155 double sugarsm = 0; // fermentable sugars in mash
156 double sugardensity = 1.611; // kg/l in solution
157 double mashtime = 0; // Total mash time
158 double mashtemp = 0; // Average mash temperature
159 double mashinfuse = 0; // Mash infuse amount
160 double colort = 0; // Colors srm * vol totals
161 double colorh = 0; // Colors ebc * vol * kt
162 double colorn = 0; // Colors ebc * pt * pct
163
164 qDebug() << "calcFermentables()";
165
166 /*
167 * Get average mashtemp and mashtime from the Mash schedule.
168 * It is possible that the schedule is not (yet) present.
169 */
170 if (product->mashs.size() > 0) {
171 for (i = 0; i < product->mashs.size(); i++) {
172 if (product->mashs.at(i).step_type == 0) // Infusion
173 mashinfuse += product->mashs.at(i).step_infuse_amount;
174 if (product->mashs.at(i).step_temp < 75) { // Ignore mashout
175 mashtime += product->mashs.at(i).step_time;
176 mashtemp += product->mashs.at(i).step_time * product->mashs.at(i).step_temp;
177 }
178 }
179 mashtemp = mashtemp / mashtime;
180 mvol = mashinfuse;
181 qDebug() << " mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
182 } else {
183 qDebug() << " no mash schedule";
184 }
185
186 const QSignalBlocker blocker1(ui->est_ogEdit);
187 const QSignalBlocker blocker2(ui->est_og2Edit);
188
189 if (product->fermentables.size() < 1) {
190 qDebug() << " no fermentables, return.";
191 product->est_og = 0.980;
192 ui->est_ogEdit->setValue(0.980);
193 ui->est_og2Edit->setValue(0.980);
194 ui->est_og3Edit->setValue(0.980);
195 ui->est_ogShow->setValue(0.980);
196 product->est_color = 0;
197 ui->est_colorEdit->setValue(0);
198 ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(0));
199 ui->est_color2Edit->setValue(0);
200 ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(0));
201 ui->est_colorShow->setValue(0);
202 ui->perc_mashShow->setValue(0);
203 ui->perc_sugarsShow->setValue(0);
204 ui->perc_caraShow->setValue(0);
205 ui->lintnerShow->setValue(0);
206 product->est_fg = 0.980;
207 ui->est_fgEdit->setValue(0.980);
208 ui->est_fg3Edit->setValue(0.980);
209 ui->est_fgShow->setValue(0.980);
210 ui->est_abvEdit->setValue(0);
211 ui->est_abv2Edit->setValue(0);
212 ui->est_abvShow->setValue(0);
213 product->est_abv = 0;
214 ui->calEdit->setValue(0);
215 product->mashs_kg = 0;
216 return;
217 }
218 qDebug() << " adjust to 100" << product->fermentables_use100;
219
220 product->mashs_kg = 0;
221 for (i = 0; i < product->fermentables.size(); i++) {
222 if (product->fermentables.at(i).f_type == 1 && product->fermentables.at(i).f_added < 4) // Sugars
223 psugar += product->fermentables.at(i).f_percentage;
224 if (product->fermentables.at(i).f_graintype == 2 && product->fermentables.at(i).f_added < 4) // Crystal/Cara
225 pcara += product->fermentables.at(i).f_percentage;
226 d = product->fermentables.at(i).f_amount * (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
227 if (product->fermentables.at(i).f_added == 0) { // Mash
228 if (mvol > 0) { // If mash volume is known
229 mvol += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_moisture / 100;
230 s += d;
231 }
232 d = ui->efficiencyEdit->value() / 100 * d;
233 sugarsm += d;
234 product->mashs_kg += product->fermentables.at(i).f_amount;
235 }
236 if (product->fermentables.at(i).f_added == 0 || product->fermentables.at(i).f_added == 1) // Mash or boil
237 sugarsf += d;
238 if (product->fermentables.at(i).f_added == 2 || product->fermentables.at(i).f_added == 3) { // Fermentation or lagering
239 x = (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
240 addedS += product->fermentables.at(i).f_amount * x;
241 addedmass += product->fermentables.at(i).f_amount;
242 vol += (x * sugardensity + (1 - x) * 1) * product->fermentables.at(i).f_amount;
243 }
244 if (product->fermentables.at(i).f_added == 0 &&
245 (product->fermentables.at(i).f_type == 0 || product->fermentables.at(i).f_type == 4) &&
246 product->fermentables.at(i).f_color < 50) {
247 lintner += product->fermentables.at(i).f_diastatic_power * product->fermentables.at(i).f_amount;
248 }
249 if (product->fermentables.at(i).f_added < 4) {
250 colort += product->fermentables.at(i).f_amount * Utils::ebc_to_srm(product->fermentables.at(i).f_color);
251 colorh += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_color * Utils::get_kt(product->fermentables.at(i).f_color);
252 colorn += (product->fermentables.at(i).f_percentage / 100) * product->fermentables.at(i).f_color; // For 8.6 Pt wort.
253 }
254 }
255 qDebug() << " colort" << colort << "colorh" << colorh << "colorn" << colorn;
256 qDebug() << " psugar" << psugar << "pcara" << pcara << "mvol" << mvol;
257 qDebug() << " sugarsf" << sugarsf << "sugarsm" << sugarsm;
258
259 double og = Utils::estimate_sg(sugarsf + addedS, product->batch_size);
260 qDebug() << " OG" << ui->est_ogEdit->value() << og;
261 product->est_og = og;
262 ui->est_ogEdit->setValue(og);
263 ui->est_og2Edit->setValue(og);
264 ui->est_og3Edit->setValue(og);
265 ui->est_ogShow->setValue(og);
266
267 product->preboil_sg = Utils::estimate_sg(sugarsm, product->boil_size);
268 qDebug() << " preboil SG" << product->preboil_sg;
269
270 /*
271 * Color of the wort
272 */
273 if (product->color_method == 4) { // Naudts
274 color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (product->boil_time / 60));
275 } else if (product->color_method == 3) { // Hans Halberstadt
276 double bv = 0.925; // Beer loss efficiency
277 double sr = 0.95; // Mash and sparge efficiency
278 color = round((4.46 * bv * sr) / product->batch_size * colorh);
279 } else {
280 double cw = colort / product->batch_size * 8.34436;
281 color = Utils::kw_to_ebc(product->color_method, cw);
282 //qDebug() << " oud EBC" << color << "new EBC" << Utils::kw_to_newebc(product->color_method, cw) << "SRM" << Utils::kw_to_srm(product->color_method, cw);
283 }
284 qDebug() << " color" << ui->est_colorEdit->value() << color << product->est_color;
285 product->est_color = color;
286 ui->est_colorEdit->setValue(color);
287 ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color));
288 ui->est_color2Edit->setValue(color);
289 ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color));
290 ui->est_colorShow->setValue(color);
291
292 /*
293 * We don't have a equipment profile in products,
294 * so we assume a certain guessed mashtun size.
295 */
296 ui->perc_mashShow->setValue(round(product->mashs_kg / (product->boil_size / 3) * 100));
297 ui->perc_sugarsShow->setValue(round(psugar));
298 ui->perc_caraShow->setValue(round(pcara));
299 if (product->mashs_kg > 0) {
300 qDebug() << " lintner" << lintner << " mashkg" << product->mashs_kg << "final" << round(lintner / product->mashs_kg);
301 ui->lintnerShow->setValue(round(lintner / product->mashs_kg));
302 } else {
303 qDebug() << " lintner N/A";
304 ui->lintnerShow->setValue(0);
305 }
306
307 /*
308 * Calculate the apparant attenuation.
309 */
310 double svg = 0;
311 if (product->yeasts.size() > 0) {
312 for (i = 0; i < product->yeasts.size(); i++) {
313 if (product->yeasts.at(i).y_use == 0) { // Used in primary
314 if (product->yeasts.at(i).y_attenuation > svg)
315 svg = product->yeasts.at(i).y_attenuation; // Take the highest if multiple yeasts.
316 }
317 // TODO: brett or others in secondary.
318 }
319 qDebug() << " SVG" << svg;
320 }
321 if (svg == 0)
322 svg = 77.0;
323 ui->est_svgEdit->setValue(svg);
324
325 double fg;
326 if (product->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
327 fg = Utils::estimate_fg(psugar, pcara, mashinfuse / product->mashs_kg, mashtime, mashtemp, svg, og);
328 else
329 fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
330 qDebug() << " FG" << ui->est_fgEdit->value() << fg;
331 product->est_fg = fg;
332 ui->est_fgEdit->setValue(fg);
333 ui->est_fg3Edit->setValue(fg);
334 ui->est_fgShow->setValue(fg);
335
336 double abv = Utils::abvol(og, fg);
337 qDebug() << " ABV" << ui->est_abvEdit->value() << abv;
338 ui->est_abvEdit->setValue(abv);
339 ui->est_abv2Edit->setValue(abv);
340 ui->est_abvShow->setValue(abv);
341 product->est_abv = abv;
342
343 /*
344 * Calculate kilocalories/liter. Formula from brouwhulp.
345 * Take the alcohol and sugar parts and then combine.
346 */
347 double alc = 1881.22 * fg * (og - fg) / (1.775 - og);
348 double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004);
349 ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296)));
350
351 // Bottle priming
352 double priming_total = 0;
353 for (i = 0; i < product->fermentables.size(); i++) {
354 if (product->fermentables.at(i).f_added == 4) {
355 priming_total += ((product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100)) *
356 product->fermentables.at(i).f_amount;
357 qDebug() << " priming" << product->fermentables.at(i).f_amount << "total" << priming_total;
358 }
359 }
360 double grl = priming_total * 1000.0 * (1 / product->batch_size);
361 double volco2 = grl * 0.510;
362 qDebug() << " priming gr/l" << grl << "volco2" << volco2;
363
364 if (volco2 > 0) {
365 product->est_carb = volco2;
366 ui->est_carbEdit->setValue(product->est_carb);
367 ui->est_carbShow->setValue(product->est_carb);
368 }
369 }
370
371
372 void EditProduct::calcFermentablesFromOG(double og)
373 {
374 qDebug() << "calcFermentablesFromOG" << og;
375
376 int i;
377 double totmass = 0;
378 double tot = 0;
379 double d, amount;
380 double efficiency = product->efficiency;
381 double sug = Utils::sg_to_plato(og) * product->batch_size * og / 100.0; // total amount of sugars in kg.
382
383 for (i = 0; i < product->fermentables.size(); i++) {
384 if (product->fermentables.at(i).f_added < 4) {
385 d = product->fermentables.at(i).f_percentage / 100.0 *
386 (product->fermentables.at(i).f_yield / 100.0) *
387 (1 - product->fermentables.at(i).f_moisture / 100.0);
388 if (product->fermentables.at(i).f_added == 0) // Mash
389 d = efficiency / 100.0 * d;
390 tot += d;
391 }
392 }
393 if (tot)
394 totmass = round((sug / tot) * 1000.0) / 1000.0;
395
396 if (totmass) {
397 for (i = 0; i < product->fermentables.size(); i++) {
398 amount = round(product->fermentables.at(i).f_percentage * 10.0 * totmass) / 1000.0;
399 product->fermentables[i].f_amount = amount;
400 }
401 }
402 }
403
404
405 void EditProduct::ferment_perc_mash_valueChanged(int value)
406 {
407 if (value < 90)
408 ui->perc_mashShow->setStyleSheet(bar_green);
409 else if (value < 100)
410 ui->perc_mashShow->setStyleSheet(bar_orange);
411 else
412 ui->perc_mashShow->setStyleSheet(bar_red);
413 }
414
415
416 void EditProduct::ferment_perc_sugars_valueChanged(int value)
417 {
418 if (value < 20)
419 ui->perc_sugarsShow->setStyleSheet(bar_green);
420 else
421 ui->perc_sugarsShow->setStyleSheet(bar_red);
422 }
423
424
425 void EditProduct::ferment_perc_cara_valueChanged(int value)
426 {
427 if (value < 25)
428 ui->perc_caraShow->setStyleSheet(bar_green);
429 else
430 ui->perc_caraShow->setStyleSheet(bar_red);
431 }
432
433
434 void EditProduct::ferment_lintner_valueChanged(int value)
435 {
436 if (value < 30)
437 ui->lintnerShow->setStyleSheet(bar_red);
438 else if (value < 40)
439 ui->lintnerShow->setStyleSheet(bar_orange);
440 else
441 ui->lintnerShow->setStyleSheet(bar_green);
442 }
443
444
445 void EditProduct::addFermentRow_clicked()
446 {
447 Fermentables newf;
448
449 qDebug() << "Add fermentable row";
450
451 for (int i = 0; i < product->fermentables.size(); i++) {
452 if (product->fermentables.at(i).f_amount == 0 && product->fermentables.at(i).f_color == 0)
453 return; // Add only one at a time.
454 }
455
456 newf.f_name = "Select one";
457 newf.f_origin = "";
458 newf.f_supplier = "";
459 newf.f_amount = 0;
460 newf.f_cost = 0;
461 newf.f_type = 0;
462 newf.f_yield = 0;
463 newf.f_color = 0;
464 newf.f_coarse_fine_diff = 0;
465 newf.f_moisture = 0;
466 newf.f_diastatic_power = 0;
467 newf.f_protein = 0;
468 newf.f_dissolved_protein = 0;
469 newf.f_max_in_batch = 100;
470 newf.f_graintype = 0;
471 newf.f_added = 0;
472 newf.f_recommend_mash = true;
473 newf.f_add_after_boil = false;
474 newf.f_adjust_to_total_100 = false;
475 newf.f_percentage = 0;
476 newf.f_di_ph = 0;
477 newf.f_acid_to_ph_57 = 0;
478
479 product->fermentables.append(newf);
480 emit refreshAll();
481 }
482
483
484 void EditProduct::deleteFermentRow_clicked()
485 {
486 if (product->locked)
487 return;
488
489 QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
490 int row = pb->objectName().toInt();
491 qDebug() << "Delete fermentable row" << row << product->fermentables.size();
492
493 if (product->fermentables.size() < 1)
494 return;
495
496 int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(product->fermentables.at(row).f_name),
497 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
498 if (rc == QMessageBox::No)
499 return;
500
501 product->fermentables.removeAt(row);
502
503 /*
504 * Recalculate the percentages on the rows left.
505 */
506 double total = 0;
507 for (int i = 0; i < product->fermentables.size(); i++)
508 if (product->fermentables.at(i).f_added < 4) // Only before bottle/kegging
509 total += product->fermentables.at(i).f_amount;
510 for (int i = 0; i < product->fermentables.size(); i++)
511 if (product->fermentables.at(i).f_added < 4)
512 product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
513
514 is_changed();
515 emit refreshAll();
516 }
517
518
519 void EditProduct::ferment_amount_changed(double val)
520 {
521 QTableWidgetItem *item;
522 double total = 0, perc;
523
524 if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4)
525 return;
526
527 qDebug() << "ferment_amount_changed()" << product->fermentables_row << val;
528
529 product->fermentables[product->fermentables_row].f_amount = val;
530 item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0'));
531 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
532 ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
533
534 for (int i = 0; i < product->fermentables.size(); i++)
535 if (product->fermentables.at(i).f_added < 4) // Only before bottle/kegging
536 total += product->fermentables.at(i).f_amount;
537 /*
538 * Recalculate the percentages
539 */
540 for (int i = 0; i < product->fermentables.size(); i++) {
541 if (product->fermentables.at(i).f_added < 4) {
542 perc = product->fermentables.at(i).f_amount / total * 100;
543 product->fermentables[i].f_percentage = perc;
544 item = new QTableWidgetItem(QString("%1%").arg(perc, 2, 'f', 1, '0'));
545 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
546 ui->fermentablesTable->setItem(i, 8, item);
547 if (i == product->fermentables_row)
548 this->pctEdit->setValue(perc);
549 }
550 }
551 is_changed();
552 }
553
554
555 void EditProduct::ferment_pct_changed(double val)
556 {
557 QTableWidgetItem *item;
558 double total = 0, row100 = -1;
559
560 if (! product->fermentables_use100)
561 return;
562
563 qDebug() << "ferment_pct_changed()" << product->fermentables_row << val;
564 /*
565 * Since we have arrived here, adjust_to_100 is active and
566 * this is not the entry to be adjusted to 100.
567 */
568 for (int i = 0; i < product->fermentables.size(); i++) {
569 if (product->fermentables.at(i).f_added < 4) // Only before bottle/kegging
570 total += product->fermentables.at(i).f_amount;
571 if (product->fermentables.at(i).f_adjust_to_total_100)
572 row100 = i;
573 }
574 double oldperc = product->fermentables.at(product->fermentables_row).f_percentage;
575 double diffp = val - oldperc;
576 double diffw = (diffp / 100) * total;
577 qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp;
578
579 product->fermentables[product->fermentables_row].f_percentage += diffp;
580 product->fermentables[product->fermentables_row].f_amount += diffw;
581 product->fermentables[row100].f_percentage -= diffp;
582 product->fermentables[row100].f_amount -= diffw;
583
584 item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[product->fermentables_row].f_amount, 4, 'f', 3, '0'));
585 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
586 ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
587 this->famountEdit->setValue(product->fermentables[product->fermentables_row].f_amount);
588
589 item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[product->fermentables_row].f_percentage, 2, 'f', 1, '0'));
590 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
591 ui->fermentablesTable->setItem(product->fermentables_row, 8, item);
592
593 item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[row100].f_amount, 4, 'f', 3, '0'));
594 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
595 ui->fermentablesTable->setItem(row100, 7, item);
596
597 item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[row100].f_percentage, 2, 'f', 1, '0'));
598 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
599 ui->fermentablesTable->setItem(row100, 8, item);
600
601 is_changed();
602 }
603
604
605 void EditProduct::ferment_to100_changed(bool val)
606 {
607 qDebug() << "ferment_to100_changed()" << product->fermentables_row << val << product->fermentables_use100;
608
609 if (product->fermentables.at(product->fermentables_row).f_added >= 4) {
610 const QSignalBlocker blocker1(to100Edit);
611 product->fermentables[product->fermentables_row].f_adjust_to_total_100 = false;
612 to100Edit->setChecked(false);
613 return;
614 }
615
616 /*
617 * Three scenario's.
618 * 1. There is no fermentable selected yet, just mark it.
619 * 2. There is current one is selected, deselect it.
620 * 3. There is another one selected, deselect and select this one.
621 */
622 if (! product->fermentables_use100 && val) {
623 /* Scenario 1. */
624 product->fermentables_use100 = true;
625 product->fermentables[product->fermentables_row].f_adjust_to_total_100 = true;
626 pctEdit->setReadOnly(false);
627 famountEdit->setReadOnly(true);
628 } else if (product->fermentables_use100 && product->fermentables[product->fermentables_row].f_adjust_to_total_100 && ! val) {
629 /* Scenario 2. */
630 product->fermentables[product->fermentables_row].f_adjust_to_total_100 = false;
631 product->fermentables_use100 = false;
632 pctEdit->setReadOnly(true);
633 famountEdit->setReadOnly(false);
634 } else if (product->fermentables_use100 && ! product->fermentables[product->fermentables_row].f_adjust_to_total_100 && val) {
635 /* Scenario 3. */
636 for (int i = 0; i < product->fermentables.size(); i++) {
637 product->fermentables[i].f_adjust_to_total_100 = false;
638 }
639 product->fermentables[product->fermentables_row].f_adjust_to_total_100 = true;
640 } else {
641 qDebug() << "bug";
642 return;
643 }
644
645 for (int i = 0; i < product->fermentables.size(); i++) {
646 to100Fermentables(i);
647 }
648 is_changed();
649 }
650
651
652 void EditProduct::ferment_select_changed(int val)
653 {
654 QSqlQuery query;
655 bool instock = finstockEdit->isChecked();
656 QString w;
657 QTableWidgetItem *item;
658
659 if (val < 1)
660 return;
661
662 qDebug() << "ferment_select_changed()" << product->fermentables_row << val << instock;
663
664 /*
665 * Search the fermentable pointed by the index and instock flag.
666 */
667 QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch,"
668 "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables ";
669 if (instock)
670 sql.append("WHERE inventory > 0 ");
671 sql.append("ORDER BY supplier,name");
672 query.prepare(sql);
673 query.exec();
674 query.first();
675 for (int i = 0; i < (val - 1); i++) {
676 query.next();
677 }
678 qDebug() << "found" << query.value(2).toString() << query.value(0).toString();
679
680 /*
681 * Replace the fermentable record contents
682 */
683 product->fermentables[product->fermentables_row].f_name = query.value(0).toString();
684 product->fermentables[product->fermentables_row].f_origin = query.value(1).toString();
685 product->fermentables[product->fermentables_row].f_supplier = query.value(2).toString();
686 product->fermentables[product->fermentables_row].f_cost = query.value(3).toDouble();
687 product->fermentables[product->fermentables_row].f_type = query.value(4).toInt();
688 product->fermentables[product->fermentables_row].f_yield = query.value(5).toDouble();
689 product->fermentables[product->fermentables_row].f_color = query.value(6).toDouble();
690 product->fermentables[product->fermentables_row].f_coarse_fine_diff = query.value(7).toDouble();
691 product->fermentables[product->fermentables_row].f_moisture = query.value(8).toDouble();
692 product->fermentables[product->fermentables_row].f_diastatic_power = query.value(9).toDouble();
693 product->fermentables[product->fermentables_row].f_protein = query.value(10).toDouble();
694 product->fermentables[product->fermentables_row].f_dissolved_protein = query.value(11).toDouble();
695 product->fermentables[product->fermentables_row].f_max_in_batch = query.value(12).toDouble();
696 product->fermentables[product->fermentables_row].f_graintype = query.value(13).toInt();
697 product->fermentables[product->fermentables_row].f_recommend_mash = query.value(14).toInt() ? true:false;
698 product->fermentables[product->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false;
699 product->fermentables[product->fermentables_row].f_di_ph = query.value(16).toDouble();
700 product->fermentables[product->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble();
701
702 /*
703 * Update the visible fields
704 */
705 fnameEdit->setText(product->fermentables.at(product->fermentables_row).f_name);
706 fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).f_supplier);
707 fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).f_max_in_batch);
708
709 ui->fermentablesTable->setItem(product->fermentables_row, 0, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).f_supplier));
710 ui->fermentablesTable->setItem(product->fermentables_row, 1, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).f_name));
711
712 w = QString("%1").arg(product->fermentables.at(product->fermentables_row).f_color, 1, 'f', 0, '0');
713 item = new QTableWidgetItem(w);
714 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
715 ui->fermentablesTable->setItem(product->fermentables_row, 2, item);
716
717 item = new QTableWidgetItem(fermentable_types[product->fermentables.at(product->fermentables_row).f_type]);
718 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
719 ui->fermentablesTable->setItem(product->fermentables_row, 3, item);
720
721 item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(product->fermentables_row).f_graintype]);
722 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
723 ui->fermentablesTable->setItem(product->fermentables_row, 4, item);
724
725 item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(product->fermentables_row).f_yield, 2, 'f', 1, '0'));
726 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
727 ui->fermentablesTable->setItem(product->fermentables_row, 6, item);
728
729 calcFermentables();
730 is_changed();
731 }
732
733
734 void EditProduct::ferment_instock_changed(bool val)
735 {
736 QSqlQuery query;
737
738 qDebug() << "ferment_instock_changed()" << product->fermentables_row << val;
739
740 this->fselectEdit->setCurrentIndex(-1);
741 this->fselectEdit->clear();
742 QString sql = "SELECT supplier,name,color,inventory FROM inventory_fermentables ";
743 if (val)
744 sql.append("WHERE inventory > 0 ");
745 sql.append("ORDER BY supplier,name");
746 query.prepare(sql);
747 query.exec();
748 query.first();
749 this->fselectEdit->addItem(""); // Start with empty value
750 for (int i = 0; i < query.size(); i++) {
751 this->fselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+" EBC) "+
752 QString("%1 kg").arg(query.value(3).toDouble(), 4, 'f', 3, '0'));
753 query.next();
754 }
755 }
756
757
758 void EditProduct::ferment_added_changed(int val)
759 {
760 qDebug() << "ferment_added_changed()" << product->fermentables_row << val;
761
762 product->fermentables[product->fermentables_row].f_added = val;
763 QTableWidgetItem *item = new QTableWidgetItem(fermentable_added[val]);
764 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
765 ui->fermentablesTable->setItem(product->fermentables_row, 5, item);
766
767 famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
768 pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4));
769
770 double total = 0;
771 for (int i = 0; i < product->fermentables.size(); i++)
772 if (product->fermentables.at(i).f_added < 4) // Only before bottle/kegging
773 total += product->fermentables.at(i).f_amount;
774 for (int i = 0; i < product->fermentables.size(); i++)
775 if (product->fermentables.at(i).f_added < 4)
776 product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
777
778 is_changed();
779 emit refreshAll();
780 }
781
782
783 void EditProduct::editFermentRow_clicked()
784 {
785 QSqlQuery query;
786
787 if (product->locked)
788 return;
789
790 QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
791 product->fermentables_row = pb->objectName().toInt();
792 qDebug() << "Edit fermentable row" << product->fermentables_row;
793 Fermentables backup = product->fermentables.at(product->fermentables_row);
794
795 QDialog* dialog = new QDialog(this);
796 dialog->resize(738, 287);
797 QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
798 buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
799 buttonBox->setGeometry(QRect(30, 240, 671, 32));
800 buttonBox->setLayoutDirection(Qt::LeftToRight);
801 buttonBox->setOrientation(Qt::Horizontal);
802 buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
803 buttonBox->setCenterButtons(true);
804 QLabel *nameLabel = new QLabel(dialog);
805 nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
806 nameLabel->setText(tr("Current ingredient:"));
807 nameLabel->setGeometry(QRect(10, 10, 141, 20));
808 nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
809 QLabel *supplierLabel = new QLabel(dialog);
810 supplierLabel->setObjectName(QString::fromUtf8("supplierLabel"));
811 supplierLabel->setText(tr("Supplier:"));
812 supplierLabel->setGeometry(QRect(10, 40, 141, 20));
813 supplierLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
814 QLabel *amountLabel = new QLabel(dialog);
815 amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
816 amountLabel->setText(tr("Amount in kg:"));
817 amountLabel->setGeometry(QRect(10, 100, 141, 20));
818 amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
819 QLabel *pctLabel = new QLabel(dialog);
820 pctLabel->setObjectName(QString::fromUtf8("pctLabel"));
821 pctLabel->setText(tr("Percentage in batch:"));
822 pctLabel->setGeometry(QRect(10, 130, 141, 20));
823 pctLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
824 QLabel *to100Label = new QLabel(dialog);
825 to100Label->setObjectName(QString::fromUtf8("to100Label"));
826 to100Label->setText(tr("Auto fill to 100%:"));
827 to100Label->setGeometry(QRect(10, 160, 141, 20));
828 to100Label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
829 QLabel *addedLabel = new QLabel(dialog);
830 addedLabel->setObjectName(QString::fromUtf8("addedLabel"));
831 addedLabel->setText(tr("Use at:"));
832 addedLabel->setGeometry(QRect(10, 190, 141, 20));
833 addedLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
834 QLabel *selectLabel = new QLabel(dialog);
835 selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
836 selectLabel->setText(tr("Select ingredient:"));
837 selectLabel->setGeometry(QRect(10, 70, 141, 20));
838 selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
839 QLabel *instockLabel = new QLabel(dialog);
840 instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
841 instockLabel->setText(tr("In stock:"));
842 instockLabel->setGeometry(QRect(525, 70, 121, 20));
843 instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
844 QLabel *maxLabel = new QLabel(dialog);
845 maxLabel->setObjectName(QString::fromUtf8("maxLabel"));
846 maxLabel->setText(tr("Max in batch:"));
847 maxLabel->setGeometry(QRect(420, 130, 121, 20));
848 maxLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
849
850 fselectEdit = new QComboBox(dialog);
851 fselectEdit->setObjectName(QString::fromUtf8("fselectEdit"));
852 fselectEdit->setGeometry(QRect(160, 70, 371, 23));
853
854 fnameEdit = new QLineEdit(dialog);
855 fnameEdit->setObjectName(QString::fromUtf8("fnameEdit"));
856 fnameEdit->setText(product->fermentables.at(product->fermentables_row).f_name);
857 fnameEdit->setGeometry(QRect(160, 10, 511, 23));
858 fnameEdit->setReadOnly(true);
859 fsupplierEdit = new QLineEdit(dialog);
860 fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit"));
861 fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).f_supplier);
862 fsupplierEdit->setGeometry(QRect(160, 40, 511, 23));
863 fsupplierEdit->setReadOnly(true);
864 famountEdit = new QDoubleSpinBox(dialog);
865 famountEdit->setObjectName(QString::fromUtf8("famountEdit"));
866 famountEdit->setGeometry(QRect(160, 100, 121, 24));
867 famountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
868 famountEdit->setAccelerated(true);
869 famountEdit->setDecimals(3);
870 famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
871 famountEdit->setMaximum(100000.0);
872 famountEdit->setSingleStep(0.0010);
873 famountEdit->setValue(product->fermentables.at(product->fermentables_row).f_amount);
874
875 pctEdit = new QDoubleSpinBox(dialog);
876 pctEdit->setObjectName(QString::fromUtf8("pctEdit"));
877 pctEdit->setGeometry(QRect(160, 130, 121, 24));
878 pctEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
879 pctEdit->setAccelerated(true);
880 pctEdit->setDecimals(1);
881 if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4) {
882 if (product->fermentables.at(product->fermentables_row).f_adjust_to_total_100)
883 pctEdit->setReadOnly(true);
884 else
885 pctEdit->setReadOnly(false);
886 } else {
887 pctEdit->setReadOnly(true);
888 }
889 pctEdit->setMaximum(100.0);
890 pctEdit->setSingleStep(0.1);
891 pctEdit->setValue(product->fermentables.at(product->fermentables_row).f_percentage);
892
893 faddedEdit = new QComboBox(dialog);
894 faddedEdit->setObjectName(QString::fromUtf8("faddedEdit"));
895 faddedEdit->setGeometry(QRect(160, 190, 161, 23));
896 faddedEdit->addItem(tr("Mash"));
897 faddedEdit->addItem(tr("Boil"));
898 faddedEdit->addItem(tr("Fermentation"));
899 faddedEdit->addItem(tr("Lagering"));
900 faddedEdit->addItem(tr("Bottle"));
901 faddedEdit->addItem(tr("Kegs"));
902 faddedEdit->setCurrentIndex(product->fermentables.at(product->fermentables_row).f_added);
903
904 to100Edit = new QCheckBox(dialog);
905 to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
906 to100Edit->setGeometry(QRect(160, 160, 85, 21));
907 to100Edit->setChecked(product->fermentables.at(product->fermentables_row).f_adjust_to_total_100);
908
909 finstockEdit = new QCheckBox(dialog);
910 finstockEdit->setObjectName(QString::fromUtf8("instockEdit"));
911 finstockEdit->setGeometry(QRect(655, 70, 85, 21));
912 finstockEdit->setChecked(true);
913
914 fmaxEdit = new QDoubleSpinBox(dialog);
915 fmaxEdit->setObjectName(QString::fromUtf8("fmaxEdit"));
916 fmaxEdit->setGeometry(QRect(550, 130, 121, 24));
917 fmaxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
918 fmaxEdit->setReadOnly(true);
919 fmaxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
920 fmaxEdit->setDecimals(1);
921 fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).f_max_in_batch);
922
923 ferment_instock_changed(true);
924
925 connect(fselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_select_changed);
926 connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_amount_changed);
927 connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_pct_changed);
928 connect(faddedEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_added_changed);
929 connect(to100Edit, &QCheckBox::stateChanged, this, &EditProduct::ferment_to100_changed);
930 connect(finstockEdit, &QCheckBox::stateChanged, this, &EditProduct::ferment_instock_changed);
931 connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
932 connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
933
934 dialog->setModal(true);
935 dialog->exec();
936 if (dialog->result() == QDialog::Rejected) {
937 qDebug() << "reject and rollback";
938 product->fermentables[product->fermentables_row] = backup;
939 /*
940 * Recalculate the percentages
941 */
942 double total = 0;
943 for (int i = 0; i < product->fermentables.size(); i++)
944 if (product->fermentables.at(i).f_added < 4) // Only before bottle/kegging
945 total += product->fermentables.at(i).f_amount;
946 product->fermentables_use100 = false;
947 for (int i = 0; i < product->fermentables.size(); i++) {
948 if (product->fermentables.at(i).f_adjust_to_total_100)
949 product->fermentables_use100 = true;
950 if (product->fermentables.at(i).f_added < 4) {
951 product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
952 }
953 }
954 }
955
956 disconnect(fselectEdit, nullptr, nullptr, nullptr);
957 disconnect(famountEdit, nullptr, nullptr, nullptr);
958 disconnect(pctEdit, nullptr, nullptr, nullptr);
959 disconnect(faddedEdit, nullptr, nullptr, nullptr);
960 disconnect(to100Edit, nullptr, nullptr, nullptr);
961 disconnect(finstockEdit, nullptr, nullptr, nullptr);
962 disconnect(buttonBox, nullptr, nullptr, nullptr);
963
964 emit refreshAll();
965 }
966
967

mercurial