src/EditProductTab6.cpp

Thu, 12 Jan 2023 14:34:14 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 12 Jan 2023 14:34:14 +0100
changeset 454
2dfead81c72f
parent 395
7212b980a527
child 455
deea5047be32
permissions
-rw-r--r--

Version 0.3.3. Removed several obsolete debug messages. IBU and Fermentation calculation debug messages are now controlled by conditional defines in global.h. In the brewday tab update the preboil and afterboil reference volumes. In the brewday tab the chiller type is a read only field directy linked to the selected equipment.

/**
 * 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("Amount"), tr("Stock"), tr("Delete"), tr("Edit") });

    ui->yeastsTable->setColumnCount(13);
    ui->yeastsTable->setColumnWidth(0, 200);	/* Yeast	*/
    ui->yeastsTable->setColumnWidth(1, 115);	/* Laboratory	*/
    ui->yeastsTable->setColumnWidth(2,  80);	/* Code		*/
    ui->yeastsTable->setColumnWidth(3,  80);	/* Type		*/
    ui->yeastsTable->setColumnWidth(4,  80);	/* 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,  80);	/* Amount	*/
    ui->yeastsTable->setColumnWidth(10, 80);	/* Stock	*/
    ui->yeastsTable->setColumnWidth(11, 80);	/* Delete	*/
    ui->yeastsTable->setColumnWidth(12, 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).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, 9, 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, 10, item);

	if (block_yeast(product->stage, product->yeasts.at(i).use)) {
	    ui->yeastsTable->removeCellWidget(i, 11);     /* to remove the unneeded button */
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Yeast already used"));
            ui->yeastsTable->setItem(i, 11, item);
            ui->yeastsTable->removeCellWidget(i, 12);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Yeast already used"));
            ui->yeastsTable->setItem(i, 12, 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, 11, pWidget);

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

    if (product->yeasts.size() == 0)
	return;		// No yeast in product.

    calcViability();

    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(((product->yeasts.at(i).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":"");

     		qDebug() << "  need" << yeast_grams << "grams, gr/hl:" << yeast_gr_hl << "pitch:" << pitch_gr_hl;
		return;
	    } else {
		/*
		 * Liquid, slant, culture etc.
		 * pitchrate see https://www.brewersfriend.com/yeast-pitch-rate-and-starter-calculator/
		 * and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
		 */
		ui->yeastProcedure->setCurrentIndex(1);
		if (product->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);
		}

		initcells = (product->yeasts.at(i).cells / 1000000) * product->yeasts.at(i).amount * (product->starter_viability / 100.0);
		if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID)
		    initcells = (product->yeasts.at(i).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 (needed > initcells) {
		    maybe_starter = true;
		}

		qDebug() << "  pitchrate:" << product->yeast_pitchrate << "needed:" << needed << "initcells:" << initcells << "starter" << maybe_starter;
	    }
	    break;
	}
    }

    if (maybe_starter != product->starter_enable) {
	product->starter_enable = maybe_starter;
	qDebug() << "  Set starter enable" << maybe_starter;
	is_changed();
    }

    if (product->starter_enable) {
	qDebug() << "  Starter calculate..";

	const QStringList labels({tr("Method"), tr("Volume"), tr("Inj. factor"), tr("New cells"), tr("Total cells"), tr("Grow factor"), "" });
	ui->starterTable->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();
    }
    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;
    qDebug() << "  calcStep(" << svol << "," << stype << "," << start << ") irate" << irate
	     << "ncells" << res.ncells << "totcells" << res.totcells << "growf" << res.growf;
    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.
	 */
	qDebug() << "  calcSteps() auto";

	if (start > needed)
	    return;

	for (step = 1; step < 5; step++) {
	    qDebug() << "  step" << 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;

	    tcells = result.totcells;
	    if (result.totcells > needed)	// Hit the target
		return;
	}

    } else {
	/*
	 * Recalculate the starter.
	 */
	qDebug() << "  calcSteps() recalculate";

	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];
	    	}
	    }
	}
    }
}


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).form == YEAST_FORMS_LIQUID) {
		vpm = 0.80;
		max = 97;
		if (product->yeasts.at(i).laboratory == "White Labs") {	// PurePitch
		    vpm = 0.95;
		    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;
	    }
	}
    }
    qDebug() << "calcViability vpm:" << vpm << "max:" << max;

    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;
    base = round(base);
    product->starter_viability = base;
    ui->conditionShow->setValue(product->starter_viability);
    qDebug() << "age" << timeDiff << "degrade" << degrade << "base" << base ;
}


void EditProduct::yeast_prod_date_changed(QDate val)
{
    product->yeast_prod_date = ui->productionEdit->nullDate();
    qDebug() << "yeast_prod_date_changed" << val << product->yeast_prod_date;
    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)
{
    qDebug() << "yeast_method_changed" << val;
    product->starter_type = val;
    calcYeast();
    is_changed();
}


void EditProduct::yeast_starter_sg_changed(double val)
{
    qDebug() << "yeast_starter_sg_changed" << val;
    product->starter_sg = val;
    calcYeast();
    is_changed();
}


void EditProduct::yeast_pitchrate_button_clicked()
{
}


void EditProduct::yeast_starter_edit_clicked()
{
    QToolButton *pb = qobject_cast<QToolButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "yeast_starter_edit_clicked" << row;

    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) {
        qDebug() << "reject";
    } else {
	product->prop_type[row] = typeEdit->currentIndex();
	product->prop_volume[row] = volEdit->value();
	qDebug() << "accept";
        calcYeast();
	is_changed();
    }

    disconnect(buttonBox, nullptr, nullptr, nullptr);
}


void EditProduct::addYeastRow_clicked()
{
    Yeasts newy;

    qDebug() << "Add yeast row";

    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();
    qDebug() << "Delete yeast row" << row << product->yeasts.size();

    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);
    is_changed();
    emit refreshAll();
}


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

    qDebug() << "yeast_amount_changed()" << product->yeasts_row << val;

    if (product->yeasts.at(product->yeasts_row).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, 9, item);

    calcYeast();
    is_changed();
}


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;

    qDebug() << "yeast_select_changed()" << product->yeasts_row << val << instock;

    /*
     * Search the yeast pointed by the index and instock flag.
     */
    QString sql = "SELECT name,laboratory,product_id,type,form,min_temperature,max_temperature,flocculation,attenuation,"
	          "cells,tolerance,sta1,bacteria,harvest_top,harvest_time,pitch_temperature,pofpos,zymocide,"
		  "gr_hl_lo,sg_lo,gr_hl_hi,sg_hi,cost,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();
    }
    qDebug() << "found" << query.value(0).toString() << query.value(2).toString();

    /*
     * Replace the yeast record contents
     */
    product->yeasts[product->yeasts_row].name = query.value(0).toString();
    product->yeasts[product->yeasts_row].laboratory = query.value(1).toString();
    product->yeasts[product->yeasts_row].product_id = query.value(2).toString();
    product->yeasts[product->yeasts_row].type = query.value(3).toInt();
    product->yeasts[product->yeasts_row].form = query.value(4).toInt();
    product->yeasts[product->yeasts_row].min_temperature = query.value(5).toDouble();
    product->yeasts[product->yeasts_row].max_temperature = query.value(6).toDouble();
    product->yeasts[product->yeasts_row].flocculation = query.value(7).toInt();
    product->yeasts[product->yeasts_row].attenuation = query.value(8).toDouble();
    product->yeasts[product->yeasts_row].cells = query.value(9).toDouble();
    product->yeasts[product->yeasts_row].tolerance = query.value(10).toDouble();
    product->yeasts[product->yeasts_row].sta1 = query.value(11).toInt() ? true:false;
    product->yeasts[product->yeasts_row].bacteria = query.value(12).toInt() ? true:false;
    product->yeasts[product->yeasts_row].harvest_top = query.value(13).toInt() ? true:false;
    product->yeasts[product->yeasts_row].harvest_time = query.value(14).toInt();
    product->yeasts[product->yeasts_row].pitch_temperature = query.value(15).toDouble();
    product->yeasts[product->yeasts_row].pofpos = query.value(16).toInt() ? true:false;
    product->yeasts[product->yeasts_row].zymocide = query.value(17).toInt();
    product->yeasts[product->yeasts_row].gr_hl_lo = query.value(18).toInt();
    product->yeasts[product->yeasts_row].sg_lo = query.value(19).toDouble();
    product->yeasts[product->yeasts_row].gr_hl_hi = query.value(20).toInt();
    product->yeasts[product->yeasts_row].sg_hi = query.value(21).toDouble();
    product->yeasts[product->yeasts_row].cost = query.value(22).toDouble();
    product->yeasts[product->yeasts_row].inventory = query.value(23).toDouble();

    /*
     * 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:"));
    } 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:"));
    }

    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).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, 9, item);

    //calcYeast();
    is_changed();
    emit refreshAll();
}


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

    qDebug() << "yeast_instock_changed()" << product->yeasts_row << val;

    this->yselectEdit->setCurrentIndex(-1);
    this->yselectEdit->clear();
    QString sql = "SELECT name,laboratory,product_id,inventory FROM inventory_yeasts ";
    if (val)
        sql.append("WHERE inventory > 0 ");
    sql.append("ORDER BY laboratory,product_id,name");
    query.prepare(sql);
    query.exec();
    query.first();
    this->yselectEdit->addItem("");      // Start with empty value
    for (int i = 0; i < query.size(); i++) {
        this->yselectEdit->addItem(query.value(1).toString()+" - "+query.value(2).toString()+" "+query.value(0).toString() + 
                        QString(" (%1 gr)").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
        query.next();
    }
}


void EditProduct::yeast_useat_changed(int val)
{
    qDebug() << "yeast_useat_changed()" << product->yeasts_row << val;

    product->yeasts[product->yeasts_row].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, 5, item);
    is_changed();
    emit refreshAll();
}


void EditProduct::editYeastRow_clicked()
{
    QSqlQuery query;

    if (product->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    product->yeasts_row = pb->objectName().toInt();
    qDebug() << "Edit yeast row" << product->yeasts_row;
    Yeasts backup = product->yeasts.at(product->yeasts_row);

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

    QLabel *nameLabel = new QLabel(dialog);
    nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
    nameLabel->setText(tr("Yeast name:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *laboratoryLabel = new QLabel(dialog);
    laboratoryLabel->setObjectName(QString::fromUtf8("laboratoryLabel"));
    laboratoryLabel->setText(tr("Laboratory:"));
    laboratoryLabel->setGeometry(QRect(10, 40, 141, 20));
    laboratoryLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *product_idLabel = new QLabel(dialog);
    product_idLabel->setObjectName(QString::fromUtf8("product_idLabel"));
    product_idLabel->setText(tr("Laboratory:"));
    product_idLabel->setGeometry(QRect(10, 70, 141, 20));
    product_idLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *selectLabel = new QLabel(dialog);
    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
    selectLabel->setText(tr("Select yeast:"));
    selectLabel->setGeometry(QRect(10,100, 141, 20));
    selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *instockLabel = new QLabel(dialog);
    instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
    instockLabel->setText(tr("In stock:"));
    instockLabel->setGeometry(QRect(525,100, 121, 20));
    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    yamountLabel = new QLabel(dialog);
    yamountLabel->setObjectName(QString::fromUtf8("amountLabel"));
    if (product->yeasts.at(product->yeasts_row).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, 130, 141, 20));
    yamountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *useatLabel = new QLabel(dialog);
    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
    useatLabel->setText(tr("Use at:"));
    useatLabel->setGeometry(QRect(10, 160, 141, 20));
    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    ynameEdit = new QLineEdit(dialog);
    ynameEdit->setObjectName(QString::fromUtf8("ynameEdit"));
    ynameEdit->setText(product->yeasts.at(product->yeasts_row).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);
    yamountEdit = new QDoubleSpinBox(dialog);
    yamountEdit->setObjectName(QString::fromUtf8("yamountEdit"));
    yamountEdit->setGeometry(QRect(160, 130, 121, 24));
    yamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    yamountEdit->setAccelerated(true);
    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, 160, 161, 23));
    useatEdit->addItem(tr("Primary"));
    useatEdit->addItem(tr("Secondary"));
    useatEdit->addItem(tr("Tertiary"));
    useatEdit->addItem(tr("Bottle"));
    useatEdit->setCurrentIndex(product->yeasts.at(product->yeasts_row).use);

    yeast_instock_changed(true);

    connect(yselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_select_changed);
    connect(yamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::yeast_amount_changed);
    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_useat_changed);
    connect(yinstockEdit, &QCheckBox::stateChanged, this, &EditProduct::yeast_instock_changed);
    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));

    dialog->setModal(true);
    dialog->exec();
    if (dialog->result() == QDialog::Rejected) {
        qDebug() << "reject and rollback";
        product->yeasts[product->yeasts_row] = backup;
    } else {

    }

    disconnect(yselectEdit, nullptr, nullptr, nullptr);
    disconnect(yamountEdit, nullptr, nullptr, nullptr);
    disconnect(useatEdit, nullptr, nullptr, nullptr);
    disconnect(yinstockEdit, nullptr, nullptr, nullptr);
    disconnect(buttonBox, nullptr, nullptr, nullptr);

    emit refreshAll();
}


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

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

    for (int i = 0; i < product->yeasts.size(); i++) {
	if (product->yeasts.at(i).form == YEAST_FORMS_DRY) { // Only adjust dry yeast
	    amount = product->yeasts.at(i).amount * factor;
	    product->yeasts[i].amount = amount;
	}
    }
}

mercurial