src/EditProductTab6.cpp

Fri, 20 Jan 2023 16:44:08 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 20 Jan 2023 16:44:08 +0100
changeset 467
c5f6f3f1b714
parent 464
1fed3ff9a64e
child 476
29487eac1183
permissions
-rw-r--r--

Added more buttons to the images tab. Load images from the database and display thumbnails added. Added support for jpeg files. Rename pictures in the database to .png. Added temporary images_list, images_count and images_current variables to the product record.

/**
 * 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()";
    qDebug() << "  " << product->prop_volume[0] << product->prop_volume[1] << product->prop_volume[2] << product->prop_volume[3];
    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();

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

#ifdef DEBUG_YEAST
     		qDebug() << "  need" << yeast_grams << "grams, gr/hl:" << yeast_gr_hl << "pitch:" << pitch_gr_hl;
#endif
		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;
		}

#ifdef DEBUG_YEAST
		qDebug() << "  pitchrate:" << product->yeast_pitchrate << "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->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;
#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++) {
	    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;
#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).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;
	    }
	}
    }
#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;
    base = round(base);
    product->starter_viability = base;
    ui->conditionShow->setValue(product->starter_viability);
#ifdef DEBUG_YEAST
    qDebug() << "age" << timeDiff << "degrade" << degrade << "base" << base ;
#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;
    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, 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;

#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();

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

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


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, 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();
#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, 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) {
#ifdef DEBUG_YEAST
        qDebug() << "reject and rollback";
#endif
        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