Thu, 12 Oct 2023 17:03:50 +0200
Changes in de iSpindel detail screen. Added a calibrate button and display the last update date and time.
/** * 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.use > D2.use) return false; if (D1.use < D2.use) return true; return (D1.amount > D2.amount); } bool EditProduct::block_yeast(int stage, int use) { if (stage > PROD_STAGE_PRIMARY && use < YEAST_USE_SECONDARY) return true; if (stage > PROD_STAGE_SECONDARY && use < YEAST_USE_TERTIARY) return true; if (stage > PROD_STAGE_TERTIARY && use < YEAST_USE_BOTTLE) return true; if (stage > PROD_STAGE_PACKAGE) return true; return false; } void EditProduct::refreshYeasts() { QString w; QWidget* pWidget; QHBoxLayout* pLayout; QTableWidgetItem *item; 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."), tr("Max."), tr("Tol."), tr("Attn."), tr("STA"), tr("Amount"), tr("Stock"), tr("Delete"), tr("Edit") }); ui->yeastsTable->setColumnCount(14); ui->yeastsTable->setColumnWidth(0, 200); /* Yeast */ ui->yeastsTable->setColumnWidth(1, 115); /* Laboratory */ ui->yeastsTable->setColumnWidth(2, 80); /* Code */ ui->yeastsTable->setColumnWidth(3, 75); /* Type */ ui->yeastsTable->setColumnWidth(4, 75); /* Usage */ ui->yeastsTable->setColumnWidth(5, 50); /* Min. */ ui->yeastsTable->setColumnWidth(6, 50); /* Max. */ ui->yeastsTable->setColumnWidth(7, 50); /* Tolerance */ ui->yeastsTable->setColumnWidth(8, 50); /* Attenuation */ ui->yeastsTable->setColumnWidth(9, 40); /* STA1 gen */ ui->yeastsTable->setColumnWidth(10, 80); /* Amount */ ui->yeastsTable->setColumnWidth(11, 80); /* Stock */ ui->yeastsTable->setColumnWidth(12, 80); /* Delete */ ui->yeastsTable->setColumnWidth(13, 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).name)); ui->yeastsTable->setItem(i, 1, new QTableWidgetItem(product->yeasts.at(i).laboratory)); ui->yeastsTable->setItem(i, 2, new QTableWidgetItem(product->yeasts.at(i).product_id)); item = new QTableWidgetItem(QCoreApplication::translate("YeastForm", g_yeast_forms[product->yeasts.at(i).form])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 3, item); item = new QTableWidgetItem(QCoreApplication::translate("YeastUse", g_yeast_use[product->yeasts.at(i).use])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 4, item); item = new QTableWidgetItem(QString("%1°C").arg(product->yeasts.at(i).min_temperature, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 5, item); item = new QTableWidgetItem(QString("%1°C").arg(product->yeasts.at(i).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).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).attenuation, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 8, item); if (product->yeasts.at(i).use != YEAST_USE_BOTTLE && product->yeasts.at(i).sta1) { QWidget *pWidget = new QWidget(); QLabel *label = new QLabel; label->setPixmap(QPixmap(":icons/silk/tick.png")); QHBoxLayout *pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(label); pLayout->setAlignment(Qt::AlignCenter); pLayout->setContentsMargins(0, 0, 0, 0); pWidget->setLayout(pLayout); ui->yeastsTable->setCellWidget(i, 9, pWidget); } else { ui->yeastsTable->removeCellWidget(i, 9); } if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID) item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).amount, 1, 'f', 0, '0')); else if (product->yeasts.at(i).form == YEAST_FORMS_DRY || product->yeasts.at(i).form == YEAST_FORMS_DRIED) item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).amount * 1000.0, 3, 'f', 2, '0')); else item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).amount * 1000.0, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 10, item); if (block_yeast(product->stage, product->yeasts.at(i).use)) { item = new QTableWidgetItem(QString("")); } else { if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID) item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).inventory, 1, 'f', 0, '0')); else if (product->yeasts.at(i).form == YEAST_FORMS_DRY || product->yeasts.at(i).form == YEAST_FORMS_DRIED) item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).inventory * 1000.0, 3, 'f', 2, '0')); else item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).inventory * 1000.0, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); if (product->yeasts.at(i).inventory < product->yeasts.at(i).amount) item->setForeground(QBrush(QColor(Qt::red))); } ui->yeastsTable->setItem(i, 11, item); if (block_yeast(product->stage, product->yeasts.at(i).use)) { ui->yeastsTable->removeCellWidget(i, 12); /* to remove the unneeded button */ item = new QTableWidgetItem(""); item->setToolTip(tr("Yeast already used")); ui->yeastsTable->setItem(i, 12, item); ui->yeastsTable->removeCellWidget(i, 13); item = new QTableWidgetItem(""); item->setToolTip(tr("Yeast already used")); ui->yeastsTable->setItem(i, 13, item); } else { 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, 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(editYeastRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_edit); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->yeastsTable->setCellWidget(i, 13, pWidget); } } } void EditProduct::initYeast() { ui->est_og4Edit->setValue(product->est_og); ui->est_fg3Edit->setValue(product->est_fg); ui->est_abv2Edit->setValue(product->est_abv); ui->productionEdit->setDate(product->yeast_prod_date); ui->conditionShow->setValue(product->starter_viability); ui->startersgEdit->setValue(product->starter_sg); ui->pitchrateEdit->setValue(product->yeast_pitchrate); ui->yeastsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); for (int i = 0; i < 3; i++) { ui->stmethodEdit->addItem(QCoreApplication::translate("YeastStarter", g_yeast_starter[i])); } ui->stmethodEdit->setCurrentIndex(product->starter_type); } /* * Calculate the needed yeast for this batch. */ void EditProduct::calcYeast() { double sg = product->brew_fermenter_sg; double use_cells; double needed = 0; double initcells = 0; bool maybe_starter = false; qDebug() << "calcYeast()"; ui->yeastProcedure->setCurrentIndex(0); if (sg <= 1.0001 && product->fg > 1.000) sg = product->fg; else if (sg <= 1.0001) sg = product->est_og; double plato = Utils::sg_to_plato(sg); double volume = product->brew_fermenter_volume; if (volume > 0) { if (product->brew_fermenter_extrawater > 0) volume += product->brew_fermenter_extrawater; } else { volume = product->batch_size - product->eq_trub_loss; } if (product->yeasts.size() == 0) return; // No yeast in product. calcViability(); double dry_amount = 0; for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).use == YEAST_USE_PRIMARY && product->yeasts.at(i).form == YEAST_FORMS_DRY) { dry_amount += product->yeasts.at(i).amount; } } for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).use == YEAST_USE_PRIMARY) { // Primary if (product->yeasts.at(i).form == YEAST_FORMS_DRY) { /* * 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).gr_hl_lo); ui->hi_gr_hlEdit->setValue(product->yeasts.at(i).gr_hl_hi); ui->lo_sgEdit->setValue(product->yeasts.at(i).sg_lo); ui->hi_sgEdit->setValue(product->yeasts.at(i).sg_hi); double og = product->yeasts.at(i).sg_lo; double f1 = product->yeasts.at(i).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) / product->starter_viability; double yeast_gr_hl = round((yeast_grams / (volume * 0.01)) * 100.0) / 100.0; double pitch_gr_hl = round(((dry_amount * 1000.0) / (volume * 0.01)) * 100.0) / 100.0; ui->dry_needShow->setValue(yeast_grams); ui->dry_pitchrateShow->setValue(yeast_gr_hl); ui->pitch_grShow->setValue(pitch_gr_hl); ui->pitch_grShow->setStyleSheet((pitch_gr_hl < yeast_gr_hl) ? "background-color: red":""); #ifdef DEBUG_YEAST qDebug() << " Need" << yeast_grams << "grams, gr/hl:" << yeast_gr_hl << "pitch:" << pitch_gr_hl; #endif calcBU(); 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->yeast_pitchrate == 0) { /* * No pitchrate yet, do a educated guess .. */ if (product->yeasts.at(i).type == YEAST_TYPES_LAGER) { product->yeast_pitchrate = 1.5; if (sg > 1.060) product->yeast_pitchrate = 2.0; } else if (product->yeasts.at(i).type == YEAST_TYPES_KVEIK) { // Real Kveik product->yeast_pitchrate = 0.075; } else { product->yeast_pitchrate = 0.75; if (sg > 1.060) product->yeast_pitchrate = 1.0; } is_changed(); ui->pitchrateEdit->setValue(product->yeast_pitchrate); #ifdef DEBUG_YEAST qDebug() << " Guessed pitchrate" << product->yeast_pitchrate; #endif } double cells = product->yeasts.at(i).cells; if (product->yeasts.at(i).yp_cells > 0) { // Use from yeastpack if set cells = product->yeasts.at(i).yp_cells; } initcells = (cells / 1000000) * product->yeasts.at(i).amount * (product->starter_viability / 100.0); if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID) initcells = (cells / 1000000000) * product->yeasts.at(i).amount * (product->starter_viability / 100.0); needed = round(product->yeast_pitchrate * volume * plato * 10.0) / 10.0; ui->neededShow->setValue(needed); if ((0.9 * needed) > initcells) { // Allow 90% underpitch without a starter maybe_starter = true; } #ifdef DEBUG_YEAST qDebug() << " Pitchrate:" << product->yeast_pitchrate << "needed:" << 0.9 * needed << "/" << needed << "initcells:" << initcells << "starter" << maybe_starter; #endif } break; } } if (maybe_starter != product->starter_enable) { if (product->starter_enable && !maybe_starter) { #ifdef DEBUG_YEAST qDebug() << " Clear obsolete starter"; #endif for (int i = 0; i < 4; i++) { product->prop_volume[i] = 0; product->prop_type[i] = 0; } } product->starter_enable = maybe_starter; #ifdef DEBUG_YEAST qDebug() << " Set starter enable" << maybe_starter; #endif is_changed(); } if (product->starter_enable) { #ifdef DEBUG_YEAST qDebug() << " Starter calculate.."; #endif const QStringList labels({tr("Method"), tr("Volume"), tr("Inj. factor"), tr("New cells"), tr("Total cells"), tr("Grow factor"), "" }); ui->starterTable->show(); ui->restartLabel->show(); ui->restartButton->show(); ui->starterTable->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->starterTable->clear(); ui->starterTable->setColumnCount(7); ui->starterTable->setRowCount(0); ui->starterTable->setColumnWidth(0, 130); /* Method */ ui->starterTable->setColumnWidth(1, 90); /* Volume */ ui->starterTable->setColumnWidth(2, 90); /* Inj. factor */ ui->starterTable->setColumnWidth(3, 90); /* New cells */ ui->starterTable->setColumnWidth(4, 90); /* Total cells */ ui->starterTable->setColumnWidth(5, 90); /* Grow factor */ ui->starterTable->setColumnWidth(6, 30); /* Edit button */ ui->starterTable->setHorizontalHeaderLabels(labels); calcSteps(product->starter_type, initcells, needed); } else { ui->starterTable->hide(); ui->restartLabel->hide(); ui->restartButton->hide(); } calcBU(); } /* * http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/ * * stype: 0=stirred, 1=shaken, 2=simple * totcells: initial cells * egrams: gram extract */ double EditProduct::getGrowthRate(int stype, double totcells, double egrams) { /* Cells per grams extract (B/g) */ double cpe = totcells / egrams; if (cpe > 3.5) return 0; // no growth if (stype == STARTERS_SIMPLE) return 0.4; // simple starter if (stype == STARTERS_SHAKEN) return 0.62; // shaken starter if (cpe <= 1.4) // stirred starter return 1.4; return 2.33 - (.67 * cpe); }; StepResult EditProduct::calcStep(double svol, int stype, double start) { StepResult res; double gperpoint = 2.72715; //number of grams of extract per point of starter gravity per liter double irate = round(start / svol * 10000.0) / 10.0; double egrams = (product->starter_sg - 1) * svol * gperpoint; double grate = getGrowthRate(stype, start, egrams); double ncells = round(egrams * grate * 10.0) / 10.0; double totcells = ncells + start; res.svol = svol; res.irate = irate; res.ncells = ncells; res.totcells = totcells; res.growf = round((ncells / start) * 100.0) / 100.0; #ifdef DEBUG_YEAST qDebug() << " calcStep(" << svol << "," << stype << "," << start << ") irate" << irate << "ncells" << res.ncells << "totcells" << res.totcells << "growf" << res.growf; #endif return res; } /* * Calculate all starter steps. * stype: final starter type: 0 = stirred, 1 = shaked, 2 = simple. * start: initial cells in billions * needed: needed cells in billions * * result: all values updated. */ void EditProduct::calcSteps(int stype, double start, double needed) { int i, step, svol; int lasti = 0; /* Erlenmeyer sizes */ const int uvols[] { 20, 40, 60, 80, 100, 150, 200, 250, 375, 500, 625, 750, 875, 1000, 1250, 1500, 2000, 2500, 3000, 4000, 5000 }; int mvols = sizeof(uvols); StepResult result; QTableWidgetItem *item; QWidget* pWidget; QHBoxLayout* pLayout; double tcells = start; QIcon iconT; iconT.addFile(QString::fromUtf8(":/icons/silk/pencil.png"), QSize(), QIcon::Normal, QIcon::Off); if ((product->prop_volume[0] + product->prop_volume[1] + product->prop_volume[2] + product->prop_volume[3]) == 0) { /* * Auto calculate the starter. */ #ifdef DEBUG_YEAST qDebug() << " calcSteps() auto"; #endif if (start > needed) return; for (step = 1; step < 5; step++) { for (i = lasti; i <= mvols; i++) { lasti = i; svol = uvols[lasti]; result = calcStep(svol, stype, tcells); if (result.irate < 25) { // inocculation rate too low, backup one step and break out. lasti = i - 1; svol = uvols[lasti]; result = calcStep(svol, stype, tcells); break; } if (result.totcells > needed || i == mvols) { // hit the target or loops done break; } } ui->starterTable->setRowCount(step); item = new QTableWidgetItem(QCoreApplication::translate("YeastStarter", g_yeast_starter[stype])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->starterTable->setItem(step -1, 0, item); item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->starterTable->setItem(step -1, 1, item); item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); if ((result.irate < 25) || (result.irate > 100)) item->setForeground(QBrush(QColor(Qt::red))); ui->starterTable->setItem(step -1, 2, item); item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->starterTable->setItem(step -1, 3, item); item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); item->setForeground(QBrush(QColor((result.totcells > needed) ? Qt::green:Qt::red))); ui->starterTable->setItem(step -1, 4, item); item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); if (result.growf < 1) item->setForeground(QBrush(QColor(Qt::red))); if ((stype > 0) && (result.growf > 3)) item->setForeground(QBrush(QColor(Qt::red))); ui->starterTable->setItem(step -1, 5, item); pWidget = new QWidget(); QToolButton* btn_edit = new QToolButton(); btn_edit->setObjectName(QString("%1").arg(step - 1)); /* Send row with the button */ btn_edit->setIcon(iconT); connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_edit); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->starterTable->setCellWidget(step -1, 6, pWidget); product->prop_type[step -1] = product->starter_type; product->prop_volume[step -1] = result.svol / 1000.0; #ifdef DEBUG_YEAST qDebug() << " step" << step << "type" << product->prop_type[step -1] << "vol" << product->prop_volume[step -1]; #endif tcells = result.totcells; if (result.totcells > needed) // Hit the target return; } } else { /* * Recalculate the starter. */ #ifdef DEBUG_YEAST qDebug() << " calcSteps() recalculate"; #endif for (step = 0; step < 4; step++) { if (product->prop_volume[step] > 0) { result = calcStep(product->prop_volume[step] * 1000, product->prop_type[step], tcells); ui->starterTable->setRowCount(step + 1); item = new QTableWidgetItem(QCoreApplication::translate("YeastStarter", g_yeast_starter[product->prop_type[step]])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->starterTable->setItem(step, 0, item); item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->starterTable->setItem(step, 1, item); item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); if ((result.irate < 25) || (result.irate > 100)) item->setForeground(QBrush(QColor(Qt::red))); ui->starterTable->setItem(step, 2, item); item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->starterTable->setItem(step, 3, item); item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); item->setForeground(QBrush(QColor((result.totcells > needed) ? Qt::green:Qt::red))); ui->starterTable->setItem(step, 4, item); item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); if (result.growf < 1) item->setForeground(QBrush(QColor(Qt::red))); if ((stype > 0) && (result.growf > 3)) item->setForeground(QBrush(QColor(Qt::red))); ui->starterTable->setItem(step, 5, item); pWidget = new QWidget(); QToolButton* btn_edit = new QToolButton(); btn_edit->setObjectName(QString("%1").arg(step)); /* Send row with the button */ btn_edit->setIcon(iconT); connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_edit); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->starterTable->setCellWidget(step, 6, pWidget); tcells = result.totcells; if (result.totcells > needed) { // Hit the target for (int i = step + 1; i < 4; i++) { product->prop_volume[i] = 0; product->prop_type[i] = 0; } return; } else if ((step < 3) && (product->prop_volume[step + 1] == 0)) { // Extra step needed, start with the same size. product->prop_volume[step + 1] = product->prop_volume[step]; product->prop_type[step + 1] = product->prop_type[step]; } #ifdef DEBUG_YEAST qDebug() << " step" << step << "type" << product->prop_type[step] << "vol" << product->prop_volume[step]; #endif } } } } void EditProduct::calcViability() { double vpm = 1.00; double max = 100; for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).use == YEAST_USE_PRIMARY) { if (! product->yeasts.at(i).yp_uuid.isNull() && (product->yeasts.at(i).yp_uuid.length() == 36)) { vpm = product->yeasts.at(i).yp_viability; max = product->yeasts.at(i).yp_max; } else if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID) { // Fallback to hardcoded values. vpm = 0.80; max = 97; if (product->yeasts.at(i).laboratory == "White Labs") { // PurePitch vpm = 0.9648; // Purepitch max = 100; } } else if (product->yeasts.at(i).form == YEAST_FORMS_DRY) { vpm = 0.998; max = 100; } else if (product->yeasts.at(i).form == YEAST_FORMS_DRIED) { // dried kveik vpm = 0.92; max = 100; } else { // Slant, Culture, Frozen, Bottle vpm = 0.99; max = 97; } } } #ifdef DEBUG_YEAST qDebug() << "calcViability vpm:" << vpm << "max:" << max; #endif double base = max; /* * Calculate time days before today. If the date is cleared, * the result is 0 days. Dates in the future are ignored. */ int timeDiff = product->yeast_prod_date.daysTo(QDate::currentDate()); if (timeDiff < 0) timeDiff == 0; double degrade = 1 - ((1 - vpm) / 30.41); // viability degradation per day. for (int i = 0; i < timeDiff; i++) { base = base * degrade; } if (base > max) base = max; product->starter_viability = round(base * 10) / 10; ui->conditionShow->setValue(product->starter_viability); #ifdef DEBUG_YEAST qDebug() << " Age" << timeDiff << "degrade" << degrade << "viability" << product->starter_viability; #endif } void EditProduct::yeast_prod_date_changed(QDate val) { product->yeast_prod_date = ui->productionEdit->nullDate(); calcViability(); calcYeast(); is_changed(); } void EditProduct::yeast_prod_date_clear() { product->yeast_prod_date = QDate(); ui->productionEdit->setDate(QDate()); } void EditProduct::yeast_prod_date_today() { product->yeast_prod_date = QDate::currentDate(); ui->productionEdit->setDate(QDate::currentDate()); } void EditProduct::yeast_method_changed(int val) { #ifdef DEBUG_YEAST qDebug() << "yeast_method_changed" << val; #endif product->starter_type = val; calcYeast(); is_changed(); } void EditProduct::yeast_starter_sg_changed(double val) { #ifdef DEBUG_YEAST qDebug() << "yeast_starter_sg_changed" << val; #endif product->starter_sg = val; calcYeast(); is_changed(); } void EditProduct::pitchindex_changed(int val) { #ifdef DEBUG_YEAST qDebug() << "pitchindex_changed" << val; #endif product->pitch_pitchindex = val; } void EditProduct::pitchrate_changed(double val) { #ifdef DEBUG_YEAST qDebug() << "pitchrate_changed" << val; #endif product->yeast_pitchrate = val; calcYeast(); is_changed(); } void EditProduct::yeast_pitchrate_button_clicked() { #ifdef DEBUG_YEAST qDebug() << "yeast_pitchrate_button_clicked"; #endif product->pitch_pitchindex = 1; QDialog* dialog = new QDialog(this); dialog->resize(420, 110); dialog->setWindowTitle(tr("BMSapp - Pitchrate")); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(30, 60, 360, 32)); buttonBox->setLayoutDirection(Qt::LeftToRight); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); buttonBox->setCenterButtons(true); QLabel *typeLabel = new QLabel(dialog); typeLabel->setObjectName(QString::fromUtf8("typeLabel")); typeLabel->setText(tr("Beer pitch type:")); typeLabel->setGeometry(QRect(10, 20, 195, 20)); typeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); QComboBox *typeEdit = new QComboBox(dialog); typeEdit->setObjectName(QString::fromUtf8("typeEdit")); typeEdit->setGeometry(QRect(215, 20, 185, 23)); typeEdit->setIconSize(QSize(0, 0)); typeEdit->addItem(tr("0.075 Real Kveik")); typeEdit->addItem(tr("0.75 Ale, upto 1.060")); typeEdit->addItem(tr("1.0 Ale, above 1.060")); typeEdit->addItem(tr("1.5 Lager, upto 1.060")); typeEdit->addItem(tr("2.0 Lager, above 1.060")); typeEdit->setCurrentIndex(product->pitch_pitchindex); connect(typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::pitchindex_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) { switch (product->pitch_pitchindex) { case 0: product->yeast_pitchrate = 0.075; break; case 1: product->yeast_pitchrate = 0.75; break; case 2: product->yeast_pitchrate = 1.0; break; case 3: product->yeast_pitchrate = 1.5; break; case 4: product->yeast_pitchrate = 2.0; break; } ui->pitchrateEdit->setValue(product->yeast_pitchrate); /* Will automatic call calcYeast() and is_changed() */ } disconnect(typeEdit, nullptr, nullptr, nullptr); disconnect(buttonBox, nullptr, nullptr, nullptr); } void EditProduct::yeast_retry_button_clicked() { #ifdef DEBUG_YEAST qDebug() << "yeast_retry_button_clicked"; #endif int rc = QMessageBox::warning(this, tr("Retry starter"), tr("Retry to automatic create starter steps"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (rc == QMessageBox::No) return; for (int i = 0; i < 4; i++) { product->prop_volume[i] = 0; product->prop_type[i] = product->starter_type; } calcYeast(); is_changed(); } void EditProduct::yeast_starter_edit_clicked() { QToolButton *pb = qobject_cast<QToolButton *>(QObject::sender()); int row = pb->objectName().toInt(); #ifdef DEBUG_YEAST qDebug() << "yeast_starter_edit_clicked" << row; #endif QDialog* dialog = new QDialog(this); dialog->resize(338, 140); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(30, 90, 271, 32)); buttonBox->setLayoutDirection(Qt::LeftToRight); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); buttonBox->setCenterButtons(true); QLabel *typeLabel = new QLabel(dialog); typeLabel->setObjectName(QString::fromUtf8("typeLabel")); typeLabel->setText(tr("Start step type:")); typeLabel->setGeometry(QRect(10, 10, 141, 20)); typeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); QLabel *volLabel = new QLabel(dialog); volLabel->setObjectName(QString::fromUtf8("volLabel")); volLabel->setText(tr("Starter step volume:")); volLabel->setGeometry(QRect(10, 40, 141, 20)); volLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); QComboBox *typeEdit = new QComboBox(dialog); typeEdit->setObjectName(QString::fromUtf8("typeEdit")); typeEdit->setGeometry(QRect(160, 10, 121, 23)); typeEdit->addItem(tr("Stirred")); typeEdit->addItem(tr("Shaken")); typeEdit->addItem(tr("Simple")); typeEdit->setCurrentIndex(product->prop_type[row]); QDoubleSpinBox *volEdit = new QDoubleSpinBox(dialog); volEdit->setObjectName(QString::fromUtf8("volEdit")); volEdit->setGeometry(QRect(160, 40, 121, 24)); volEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); volEdit->setAccelerated(true); volEdit->setDecimals(3); volEdit->setSingleStep(0.01); volEdit->setValue(product->prop_volume[row]); volEdit->setMaximum(5); connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); dialog->setModal(true); dialog->exec(); if (dialog->result() != QDialog::Rejected) { product->prop_type[row] = typeEdit->currentIndex(); product->prop_volume[row] = volEdit->value(); calcYeast(); is_changed(); } disconnect(buttonBox, nullptr, nullptr, nullptr); } void EditProduct::addYeastRow_clicked() { Yeasts newy; #ifdef DEBUG_YEAST qDebug() << "Add yeast row"; #endif for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).amount == 0) return; // Add only one at a time. } newy.name = "Select one"; newy.laboratory = ""; newy.product_id = ""; newy.amount = 0; newy.type = YEAST_TYPES_ALE; newy.form = YEAST_FORMS_LIQUID; newy.min_temperature = 0; newy.max_temperature = 0; newy.flocculation = 0; newy.attenuation = 0; newy.cells = 0; newy.tolerance = 0; newy.inventory = 0; newy.use = YEAST_USE_PRIMARY; newy.sta1 = false; newy.bacteria = false; newy.harvest_top = false; newy.harvest_time = 0; newy.pitch_temperature = 0; newy.pofpos = false; newy.zymocide = 0; newy.gr_hl_lo = 0; newy.sg_lo = 0; newy.gr_hl_hi = 0; newy.sg_hi = 0; newy.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(); #ifdef DEBUG_YEAST qDebug() << "Delete yeast row" << row << product->yeasts.size(); #endif int rc = QMessageBox::warning(this, tr("Delete yeast"), tr("Delete %1").arg(product->yeasts.at(row).name), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (rc == QMessageBox::No) return; product->yeasts.removeAt(row); bool primary = false; /* Check if any primary yeast is left */ for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).use == YEAST_USE_PRIMARY) primary = true; } if (! primary) { #ifdef DEBUG_YEAST qDebug() << " Clear starter (if any)"; #endif for (int i = 0; i < 4; i++) product->prop_volume[i] = 0; } is_changed(); emit refreshAll(); } void EditProduct::yeast_amount_changed(double val) { QTableWidgetItem *item; #ifdef DEBUG_YEAST qDebug() << "yeast_amount_changed()" << product->yeasts_row << val; #endif if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_LIQUID) { product->yeasts[product->yeasts_row].amount = val; item = new QTableWidgetItem(QString("%1 pack").arg(val, 1, 'f', 0, '0')); } else if ((product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRY) || (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRIED)) { product->yeasts[product->yeasts_row].amount = val / 1000.0; item = new QTableWidgetItem(QString("%1 gr").arg(val, 3, 'f', 2, '0')); } else { product->yeasts[product->yeasts_row].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, 10, item); calcYeast(); is_changed(); } void EditProduct::yeast_load_packages(QString laboratory, int form) { QSqlQuery query; /* * Clear package and rebuild package select table. */ this->ypackageEdit->setCurrentIndex(-1); this->ypackageEdit->clear(); this->ypackageEdit->addItem(""); // Start with empty value query.prepare("SELECT * FROM inventory_yeastpack WHERE laboratory=:laboratory AND form=:form AND valid='1' ORDER BY laboratory,package"); query.bindValue(":laboratory", laboratory); query.bindValue(":form", form); // qDebug() << " search" << laboratory << form; query.exec(); while (query.next()) { this->ypackageEdit->addItem(query.value("laboratory").toString()+" - "+query.value("package").toString()); // qDebug() << " add package" << query.value("laboratory").toString() << query.value("package").toString(); } } void EditProduct::yeast_package_changed(int val) { QSqlQuery query; int index = 0; #ifdef DEBUG_YEAST qDebug() << "yeast_package_changed()" << product->yeasts_row << val; #endif /* * Scan the packages for result number 'val' */ query.prepare("SELECT * FROM inventory_yeastpack WHERE laboratory=:laboratory AND form=:form AND valid='1' ORDER BY laboratory,package"); query.bindValue(":laboratory", product->yeasts.at(product->yeasts_row).laboratory); query.bindValue(":form", product->yeasts.at(product->yeasts_row).form); // qDebug() << " search" << product->yeasts.at(product->yeasts_row).laboratory << product->yeasts.at(product->yeasts_row).form; query.exec(); while (query.next()) { index++; if (index == val) { // qDebug() << " result" << index << query.value("package").toString(); product->yeasts[product->yeasts_row].yp_uuid = query.value("uuid").toString(); product->yeasts[product->yeasts_row].yp_package = query.value("package").toString(); product->yeasts[product->yeasts_row].yp_cells = query.value("cells").toDouble(); product->yeasts[product->yeasts_row].yp_viability = query.value("viability").toDouble(); product->yeasts[product->yeasts_row].yp_max = query.value("max").toInt(); product->yeasts[product->yeasts_row].yp_size = query.value("size").toDouble(); is_changed(); return; } } if (! product->yeasts[product->yeasts_row].yp_uuid.isNull()) { /* * Clear package */ product->yeasts[product->yeasts_row].yp_uuid = QString(); product->yeasts[product->yeasts_row].yp_package = QString(); product->yeasts[product->yeasts_row].yp_cells = product->yeasts[product->yeasts_row].cells; product->yeasts[product->yeasts_row].yp_viability = 0.99; product->yeasts[product->yeasts_row].yp_max = 100; product->yeasts[product->yeasts_row].yp_size = 0.01; is_changed(); // qDebug() << " cleared old yp_xxx data"; } // qDebug() << " result not found"; } void EditProduct::yeast_select_changed(int val) { QSqlQuery query; bool instock = yinstockEdit->isChecked(); QString w; QTableWidgetItem *item; int oldform = product->yeasts.at(product->yeasts_row).form; if (val < 1) return; #ifdef DEBUG_YEAST qDebug() << "yeast_select_changed()" << product->yeasts_row << val << instock; #endif /* * 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,inventory 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(); } #ifdef DEBUG_YEAST qDebug() << "found" << query.value("name").toString() << query.value("product_id").toString(); #endif /* * Replace the yeast record contents */ product->yeasts[product->yeasts_row].name = query.value("name").toString(); product->yeasts[product->yeasts_row].laboratory = query.value("laboratory").toString(); product->yeasts[product->yeasts_row].product_id = query.value("product_id").toString(); product->yeasts[product->yeasts_row].type = query.value("type").toInt(); product->yeasts[product->yeasts_row].form = query.value("form").toInt(); product->yeasts[product->yeasts_row].min_temperature = query.value("min_temperature").toDouble(); product->yeasts[product->yeasts_row].max_temperature = query.value("max_temperature").toDouble(); product->yeasts[product->yeasts_row].flocculation = query.value("flocculation").toInt(); product->yeasts[product->yeasts_row].attenuation = query.value("attenuation").toDouble(); product->yeasts[product->yeasts_row].cells = query.value("cells").toDouble(); product->yeasts[product->yeasts_row].tolerance = query.value("tolerance").toDouble(); product->yeasts[product->yeasts_row].sta1 = query.value("sta1").toInt() ? true:false; product->yeasts[product->yeasts_row].bacteria = query.value("bacteria").toInt() ? true:false; product->yeasts[product->yeasts_row].harvest_top = query.value("harvest_top").toInt() ? true:false; product->yeasts[product->yeasts_row].harvest_time = query.value("harvest_time").toInt(); product->yeasts[product->yeasts_row].pitch_temperature = query.value("pitch_temperature").toDouble(); product->yeasts[product->yeasts_row].pofpos = query.value("pofpos").toInt() ? true:false; product->yeasts[product->yeasts_row].zymocide = query.value("zymocide").toInt(); product->yeasts[product->yeasts_row].gr_hl_lo = query.value("gr_hl_lo").toInt(); product->yeasts[product->yeasts_row].sg_lo = query.value("sg_lo").toDouble(); product->yeasts[product->yeasts_row].gr_hl_hi = query.value("gr_hl_hi").toInt(); product->yeasts[product->yeasts_row].sg_hi = query.value("sg_hi").toDouble(); product->yeasts[product->yeasts_row].cost = query.value("cost").toDouble(); product->yeasts[product->yeasts_row].inventory = query.value("inventory").toDouble(); product->yeasts[product->yeasts_row].yp_uuid = QString(); /* * Update the visible fields */ const QSignalBlocker blocker1(yamountEdit); ynameEdit->setText(product->yeasts.at(product->yeasts_row).name); ylaboratoryEdit->setText(product->yeasts.at(product->yeasts_row).laboratory); yproduct_idEdit->setText(product->yeasts.at(product->yeasts_row).product_id); if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_LIQUID) { if (oldform != YEAST_FORMS_LIQUID) product->yeasts[product->yeasts_row].amount = 1; yamountEdit->setValue(product->yeasts[product->yeasts_row].amount); yamountEdit->setDecimals(0); yamountEdit->setSingleStep(1.0); yamountLabel->setText(tr("Total packs:")); product->yeast_pitchrate = 0; } else if ((product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRY) || (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRIED)) { if (oldform == YEAST_FORMS_LIQUID) product->yeasts[product->yeasts_row].amount = 0.01; yamountEdit->setValue(product->yeasts[product->yeasts_row].amount * 1000.0); yamountEdit->setDecimals(1); yamountEdit->setSingleStep(0.5); yamountLabel->setText(tr("Amount in gr:")); } else { if (oldform == YEAST_FORMS_LIQUID) product->yeasts[product->yeasts_row].amount = 0.01; yamountEdit->setValue(product->yeasts[product->yeasts_row].amount * 1000.0); yamountEdit->setDecimals(1); yamountEdit->setSingleStep(0.5); yamountLabel->setText(tr("Amount in ml:")); product->yeast_pitchrate = 0; } /* * Clear package and rebuild package select table. */ yeast_load_packages(query.value("laboratory").toString(), query.value("form").toInt()); ui->yeastsTable->setItem(product->yeasts_row, 0, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).name)); ui->yeastsTable->setItem(product->yeasts_row, 1, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).laboratory)); ui->yeastsTable->setItem(product->yeasts_row, 2, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).product_id)); item = new QTableWidgetItem(QCoreApplication::translate("YeastForm", g_yeast_forms[product->yeasts.at(product->yeasts_row).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).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).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).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).attenuation, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(product->yeasts_row, 8, item); if (product->yeasts.at(product->yeasts_row).use != YEAST_USE_BOTTLE && product->yeasts.at(product->yeasts_row).sta1) { QWidget *pWidget = new QWidget(); QLabel *label = new QLabel; label->setPixmap(QPixmap(":icons/silk/tick.png")); QHBoxLayout *pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(label); pLayout->setAlignment(Qt::AlignCenter); pLayout->setContentsMargins(0, 0, 0, 0); pWidget->setLayout(pLayout); ui->yeastsTable->setCellWidget(product->yeasts_row, 9, pWidget); } else { ui->yeastsTable->removeCellWidget(product->yeasts_row, 9); } if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_LIQUID) item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(product->yeasts_row).amount, 1, 'f', 0, '0')); else if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRY || product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRIED) item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(product->yeasts_row).amount * 1000.0, 3, 'f', 2, '0')); else item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(product->yeasts_row).amount * 1000.0, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(product->yeasts_row, 10, item); /* * If there is no need for a starter, wipe it. */ bool maybe_starter = false; for (int i = 0; i < product->yeasts.size(); i++) { if (product->yeasts.at(i).use == YEAST_USE_PRIMARY && ! ((product->yeasts.at(i).form == YEAST_FORMS_DRY) || (product->yeasts.at(i).form == YEAST_FORMS_DRIED))) maybe_starter = true; } if (! maybe_starter) { #ifdef DEBUG_YEAST qDebug() << " Clear starter (if any)"; #endif for (int i = 0; i < 4; i++) product->prop_volume[i] = 0; } is_changed(); } void EditProduct::yeast_instock_changed(bool val) { QSqlQuery query; #ifdef DEBUG_YEAST qDebug() << "yeast_instock_changed()" << product->yeasts_row << val; #endif 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) { #ifdef DEBUG_YEAST qDebug() << "yeast_useat_changed()" << product->yeasts_row << val; #endif product->yeasts[product->yeasts_row].use = val; QTableWidgetItem *item = new QTableWidgetItem(QCoreApplication::translate("YeastUse", g_yeast_use[val])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->yeastsTable->setItem(product->yeasts_row, 4, item); is_changed(); } void EditProduct::editYeastRow_clicked() { QSqlQuery query; if (product->locked) return; QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); product->yeasts_row = pb->objectName().toInt(); #ifdef DEBUG_YEAST qDebug() << "Edit yeast row" << product->yeasts_row; #endif Yeasts backup = product->yeasts.at(product->yeasts_row); QDialog* dialog = new QDialog(this); dialog->resize(738, 290); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(30, 240, 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); QLabel *packageLabel = new QLabel(dialog); packageLabel->setObjectName(QString::fromUtf8("packageLabel")); packageLabel->setText(tr("Select package:")); packageLabel->setGeometry(QRect(10,130, 141, 20)); packageLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); yamountLabel = new QLabel(dialog); yamountLabel->setObjectName(QString::fromUtf8("amountLabel")); if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_LIQUID) yamountLabel->setText(tr("Total packs:")); else if ((product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRY) || (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRIED)) yamountLabel->setText(tr("Amount in gr:")); else yamountLabel->setText(tr("Amount in ml:")); yamountLabel->setGeometry(QRect(10, 160, 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, 190, 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).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).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).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); ypackageEdit = new QComboBox(dialog); ypackageEdit->setObjectName(QString::fromUtf8("packageEdit")); ypackageEdit->setGeometry(QRect(160,130, 371, 23)); yamountEdit = new QDoubleSpinBox(dialog); yamountEdit->setObjectName(QString::fromUtf8("yamountEdit")); yamountEdit->setGeometry(QRect(160, 160, 121, 24)); yamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); yamountEdit->setAccelerated(true); yamountEdit->setMaximum(10000.0); if (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_LIQUID) { yamountEdit->setDecimals(0); yamountEdit->setSingleStep(1.0); yamountEdit->setValue(product->yeasts.at(product->yeasts_row).amount); } else if ((product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRY) || (product->yeasts.at(product->yeasts_row).form == YEAST_FORMS_DRIED)) { yamountEdit->setDecimals(1); yamountEdit->setSingleStep(0.5); yamountEdit->setValue(product->yeasts.at(product->yeasts_row).amount * 1000.0); } else { yamountEdit->setDecimals(1); yamountEdit->setSingleStep(0.5); yamountEdit->setValue(product->yeasts.at(product->yeasts_row).amount * 1000.0); } yamountEdit->setMaximum(1000000000.0); useatEdit = new QComboBox(dialog); useatEdit->setObjectName(QString::fromUtf8("useatEdit")); useatEdit->setGeometry(QRect(160, 190, 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).use); yeast_instock_changed(true); yeast_load_packages(product->yeasts.at(product->yeasts_row).laboratory, product->yeasts.at(product->yeasts_row).form); /* * Try to set the yeastpack dropdown to the selected entry if set. */ int index = 0; if (! product->yeasts.at(product->yeasts_row).yp_uuid.isNull() && (product->yeasts.at(product->yeasts_row).yp_uuid.length() == 36)) { query.prepare("SELECT * FROM inventory_yeastpack WHERE laboratory=:laboratory AND form=:form AND valid='1' ORDER BY laboratory,package"); query.bindValue(":laboratory", product->yeasts.at(product->yeasts_row).laboratory); query.bindValue(":form", product->yeasts.at(product->yeasts_row).form); query.exec(); while (query.next()) { index++; if (query.value("uuid").toString() == product->yeasts.at(product->yeasts_row).yp_uuid) { this->ypackageEdit->setCurrentIndex(index); break; } } } connect(yselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_select_changed); connect(ypackageEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_package_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) { #ifdef DEBUG_YEAST qDebug() << "reject and rollback"; #endif product->yeasts[product->yeasts_row] = backup; } else { } disconnect(yselectEdit, nullptr, nullptr, nullptr); disconnect(ypackageEdit, 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).form == YEAST_FORMS_DRY) { // Only adjust dry yeast amount = product->yeasts.at(i).amount * factor; product->yeasts[i].amount = amount; } } }