src/EditProduct.cpp

Sun, 15 May 2022 23:10:15 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 15 May 2022 23:10:15 +0200
changeset 210
b45bd6da5220
parent 209
19c50b1f58d3
child 212
8b84dd3579ef
permissions
-rw-r--r--

Implemented brewday change cooling, aeration, whirlpool and fermenter volumes values. All editable values on the brewday tab are now functional.

/**
 * EditProduct.cpp is part of bmsapp.
 *
 * 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 hope 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/>.
 */
#include "MainWindow.h"
#include "EditProduct.h"
#include "PrinterDialog.h"
#include "../ui/ui_EditProduct.h"
#include "Utils.h"
#include "global.h"



EditProduct::EditProduct(int id, QWidget *parent) : QDialog(parent), ui(new Ui::EditProduct)
{
    QSqlQuery query, wquery, yquery;

    qDebug() << "EditProduct record:" << id;
    product = new Product;
    ui->setupUi(this);
    product->fermentables_row = product->hops_row = product->miscs_row = product->yeasts_row = product->mashs_row = -1;
    product->fermentables_use100 = false;
    this->recno = id;

    WindowTitle();

    ui->typeEdit->addItem(tr("Extract"));
    ui->typeEdit->addItem(tr("Partial Mash"));
    ui->typeEdit->addItem(tr("All Grain"));

    ui->color_methodEdit->addItem("Morey");
    ui->color_methodEdit->addItem("Mosher");
    ui->color_methodEdit->addItem("Daniels");
    ui->color_methodEdit->addItem("Halberstadt");
    ui->color_methodEdit->addItem("Naudts");

    ui->ibu_methodEdit->addItem("Tinseth");
    ui->ibu_methodEdit->addItem("Rager");
    ui->ibu_methodEdit->addItem("Daniels");

    for (int i = 0; i < my_acids.size(); i++) {
	ui->mw_acidPick->addItem(my_acids.at(i).name_en);
	ui->sp_acidtypeEdit->addItem(my_acids.at(i).name_en);
    }

    ui->sp_sourceEdit->addItem(tr("Source 1"));
    ui->sp_sourceEdit->addItem(tr("Source 2"));
    ui->sp_sourceEdit->addItem(tr("Mixed"));

    query.prepare("SELECT name FROM inventory_waters ORDER BY record");
    query.exec();
    ui->w1_nameEdit->addItem("");
    ui->w2_nameEdit->addItem("");
    while (query.next()) {
	ui->w1_nameEdit->addItem(query.value(0).toString());
	ui->w2_nameEdit->addItem(query.value(0).toString());
    }

    query.prepare("SELECT name FROM profile_water ORDER BY name");
    query.exec();
    ui->wt_sourceEdit->addItem("");
    while (query.next()) {
        ui->wt_sourceEdit->addItem(query.value(0).toString());
    }

    query.prepare("SELECT name FROM profile_mash ORDER BY name");
    query.exec();
    ui->mash_pickEdit->addItem("");
    while (query.next()) {
	ui->mash_pickEdit->addItem(query.value(0).toString());
    }

    ui->beerstyleEdit->addItem(product->st_name);	// First add a dummy, the current style
    query.prepare("SELECT style_guide,style_letter,name FROM profile_styles ORDER BY style_guide,style_letter,name");
    query.exec();
    while (query.next()) {
	ui->beerstyleEdit->addItem(query.value(0).toString()+" "+query.value(1).toString()+" "+query.value(2).toString());
    }

    ui->brew_coolwithEdit->addItem("-");
    ui->brew_coolwithEdit->addItem(tr("Emersion chiller"));
    ui->brew_coolwithEdit->addItem(tr("Counterflow chiller"));
    ui->brew_coolwithEdit->addItem(tr("Au bain marie"));
    ui->brew_coolwithEdit->addItem(tr("Natural"));

    ui->brew_aerwithEdit->addItem(tr("None"));
    ui->brew_aerwithEdit->addItem(tr("Air"));
    ui->brew_aerwithEdit->addItem(tr("Oxygen"));

    ui->bottle_sugarEdit->addItem("");
    ui->keg_sugarEdit->addItem("");
    query.prepare("SELECT name FROM inventory_fermentables WHERE type = '1' OR type = '3' ORDER BY name");	// Sugars or dry extract
    query.exec();
    while (query.next()) {
	ui->bottle_sugarEdit->addItem(query.value(0).toString());
	ui->keg_sugarEdit->addItem(query.value(0).toString());
    }

    if (id >= 0) {
	query.prepare("SELECT * FROM products WHERE record = :recno");
	query.bindValue(":recno", id);
	query.exec();
	if (! query.first()) {
	    qDebug() << "EditProduct seek error record" << id;
	    QMessageBox::warning(this, tr("Database error"), tr("MySQL error: record %1 not found").arg(id));
	    this->done(QDialog::Rejected);	// At this stage, this doesn't work because the dialog is not yet visible.
	    return;
	}

	QSqlRecord rec = query.record();
	for (int i = 0; i < rec.count(); i++)
            qDebug() << i << rec.fieldName(i) << query.value(i);

	product->record = query.value("record").toInt();
	product->uuid = query.value("uuid").toString();
	product->name = query.value("name").toString();
	product->code = query.value("code").toString();
	product->birth = query.value("birth").toDate();
	product->stage = query.value("stage").toInt();
        product->notes = query.value("notes").toString();
	product->log_brew = query.value("log_brew").toInt() ? true:false;
	product->log_fermentation = query.value("log_fermentation").toInt() ? true:false;
	product->log_ispindel = query.value("log_ispindel").toInt() ? true:false;
	product->log_co2pressure = query.value("log_co2pressure").toInt() ? true:false;
	product->inventory_reduced = query.value("inventory_reduced").toInt();
	product->locked = query.value("locked").toInt() ? true:false;

	product->eq_name = query.value("eq_name").toString();
	product->eq_notes = query.value("eq_notes").toString();
	product->eq_boil_size = query.value("eq_boil_size").toDouble();
	product->eq_batch_size = query.value("eq_batch_size").toDouble();
	product->eq_tun_volume = query.value("eq_tun_volume").toDouble();
	product->eq_tun_weight = query.value("eq_tun_weight").toDouble();
	product->eq_tun_specific_heat = query.value("eq_tun_specific_heat").toDouble();
	product->eq_tun_material = query.value("eq_tun_material").toInt();
	product->eq_tun_height = query.value("eq_tun_height").toDouble();
	product->eq_top_up_water = query.value("eq_top_up_water").toDouble();
	product->eq_trub_chiller_loss = query.value("eq_trub_chiller_loss").toDouble();
	product->eq_evap_rate = query.value("eq_evap_rate").toDouble();
	product->eq_boil_time = query.value("eq_boil_time").toDouble();
	product->eq_calc_boil_volume = query.value("eq_calc_boil_volume").toInt() ? true:false;
	product->eq_top_up_kettle = query.value("eq_top_up_kettle").toDouble();
	product->eq_hop_utilization = query.value("eq_hop_utilization").toDouble();
	product->eq_lauter_volume = query.value("eq_lauter_volume").toDouble();
	product->eq_lauter_height = query.value("eq_lauter_height").toDouble();
	product->eq_lauter_deadspace = query.value("eq_lauter_deadspace").toDouble();
	product->eq_kettle_volume = query.value("eq_kettle_volume").toDouble();
	product->eq_kettle_height = query.value("eq_kettle_height").toDouble();
	product->eq_mash_volume = query.value("eq_mash_volume").toDouble();
	product->eq_mash_max = query.value("eq_mash_max").toDouble();
	product->eq_efficiency = query.value("eq_efficiency").toDouble();

	product->brew_date_start = query.value("brew_date_start").toDateTime();
	product->brew_mash_ph = query.value("brew_mash_ph").toDouble();
	product->brew_mash_sg = query.value("brew_mash_sg").toDouble();
	product->brew_mash_efficiency = query.value("brew_mash_efficiency").toDouble();
	product->brew_sparge_temperature = query.value("brew_sparge_temperature").toDouble();
	product->brew_sparge_volume = query.value("brew_sparge_volume").toDouble();
	product->brew_sparge_est = query.value("brew_sparge_est").toDouble();
	product->brew_sparge_ph = query.value("brew_sparge_ph").toDouble();
	product->brew_preboil_volume = query.value("brew_preboil_volume").toDouble();
	product->brew_preboil_sg = query.value("brew_preboil_sg").toDouble();
	product->brew_preboil_ph = query.value("brew_preboil_ph").toDouble();
	product->brew_preboil_efficiency = query.value("brew_preboil_efficiency").toDouble();
	product->brew_aboil_volume = query.value("brew_aboil_volume").toDouble();
	product->brew_aboil_sg = query.value("brew_aboil_sg").toDouble();
	product->brew_aboil_ph = query.value("brew_aboil_ph").toDouble();
	product->brew_aboil_efficiency = query.value("brew_aboil_efficiency").toDouble();
	product->brew_cooling_method = query.value("brew_cooling_method").toInt();
	product->brew_cooling_time = query.value("brew_cooling_time").toDouble();
	product->brew_cooling_to = query.value("brew_cooling_to").toDouble();
	product->brew_whirlpool9 = query.value("brew_whirlpool9").toDouble();
	product->brew_whirlpool7 = query.value("brew_whirlpool7").toDouble();
	product->brew_whirlpool6 = query.value("brew_whirlpool6").toDouble();
	product->brew_whirlpool2 = query.value("brew_whirlpool2").toDouble();
	product->brew_fermenter_volume = query.value("brew_fermenter_volume").toDouble();
	product->brew_fermenter_extrawater = query.value("brew_fermenter_extrawater").toDouble();
	product->brew_fermenter_tcloss = query.value("brew_fermenter_tcloss").toDouble();
	product->brew_aeration_time = query.value("brew_aeration_time").toDouble();
	product->brew_aeration_speed = query.value("brew_aeration_speed").toDouble();
	product->brew_aeration_type = query.value("brew_aeration_type").toInt();
	product->brew_fermenter_sg = query.value("brew_fermenter_sg").toDouble();
	product->brew_fermenter_ibu = query.value("brew_fermenter_ibu").toDouble();
	product->brew_fermenter_color = query.value("brew_fermenter_color").toDouble();
	product->brew_date_end = query.value("brew_date_end").toDateTime();

	product->og = query.value("og").toDouble();
	product->fg = query.value("fg").toDouble();
	product->primary_start_temp = query.value("primary_start_temp").toDouble();
	product->primary_max_temp = query.value("primary_max_temp").toDouble();
	product->primary_end_temp = query.value("primary_end_temp").toDouble();
	product->primary_end_sg = query.value("primary_end_sg").toDouble();
	product->primary_end_date = query.value("primary_end_date").toDate();
	product->secondary_temp = query.value("secondary_temp").toDouble();
	product->secondary_end_sg = query.value("secondary_end_sg").toDouble();
	product->secondary_end_date = query.value("secondary_end_date").toDate();
	product->tertiary_temp = query.value("tertiary_temp").toDouble();

	product->package_date = query.value("package_date").toDate();
	product->package_volume = query.value("package_volume").toDouble();
	product->package_infuse_amount = query.value("package_infuse_amount").toDouble();
	product->package_infuse_abv = query.value("package_infuse_abv").toDouble();
	product->package_infuse_notes = query.value("package_infuse_notes").toString();
	product->package_abv = query.value("package_abv").toDouble();
	product->package_ph = query.value("package_ph").toDouble();

	product->bottle_amount = query.value("bottle_amount").toDouble();
	product->bottle_carbonation = query.value("bottle_carbonation").toDouble();
	product->bottle_priming_sugar = query.value("bottle_priming_sugar").toInt();
	product->bottle_priming_amount = query.value("bottle_priming_amount").toDouble();
	product->bottle_priming_water = query.value("bottle_priming_water").toDouble();
	product->bottle_carbonation_temp = query.value("bottle_carbonation_temp").toDouble();

	product->keg_amount = query.value("keg_amount").toDouble();
	product->keg_carbonation = query.value("keg_carbonation").toDouble();
	product->keg_priming_sugar = query.value("keg_priming_sugar").toInt();
	product->keg_priming_amount = query.value("keg_priming_amount").toDouble();
	product->keg_priming_water = query.value("keg_priming_water").toDouble();
	product->keg_carbonation_temp = query.value("keg_carbonation_temp").toDouble();
	product->keg_forced_carb = query.value("keg_forced_carb").toInt() ? true:false;
	product->keg_pressure = query.value("keg_pressure").toDouble();

	product->taste_notes = query.value("taste_notes").toString();
	product->taste_rate = query.value("taste_rate").toDouble();
	product->taste_date = query.value("taste_date").toDate();
	product->taste_color = query.value("taste_color").toString();
	product->taste_transparency = query.value("taste_transparency").toString();
	product->taste_head = query.value("taste_head").toString();
	product->taste_aroma = query.value("taste_aroma").toString();
	product->taste_taste = query.value("taste_taste").toString();
	product->taste_mouthfeel = query.value("taste_mouthfeel").toString();
	product->taste_aftertaste = query.value("taste_aftertaste").toString();

	product->st_name = query.value("st_name").toString();
	product->st_letter = query.value("st_letter").toString();
	product->st_guide = query.value("st_guide").toString();
	product->st_category = query.value("st_category").toString();
	product->st_category_number = query.value("st_category_number").toInt();
	product->st_type = query.value("st_type").toInt();
	product->st_og_min = query.value("st_og_min").toDouble();
	product->st_og_max = query.value("st_og_max").toDouble();
	product->st_fg_min = query.value("st_fg_min").toDouble();
	product->st_fg_max = query.value("st_fg_max").toDouble();
	product->st_ibu_min = query.value("st_ibu_min").toDouble();
	product->st_ibu_max = query.value("st_ibu_max").toDouble();
	product->st_color_min = query.value("st_color_min").toDouble();
	product->st_color_max = query.value("st_color_max").toDouble();
	product->st_carb_min = query.value("st_carb_min").toDouble();
	product->st_carb_max = query.value("st_carb_max").toDouble();
	product->st_abv_min = query.value("st_abv_min").toDouble();
	product->st_abv_max = query.value("st_abv_max").toDouble();

	product->type = query.value("type").toInt();
	product->batch_size = query.value("batch_size").toDouble();
	product->boil_size = query.value("boil_size").toDouble();
	product->boil_time = query.value("boil_time").toDouble();
	product->efficiency = query.value("efficiency").toDouble();
	product->est_og = query.value("est_og").toDouble();
	product->est_og3 = query.value("est_og3").toDouble();
	product->est_fg = query.value("est_fg").toDouble();
	product->est_abv = query.value("est_abv").toDouble();
	product->est_color = query.value("est_color").toDouble();
	product->color_method = query.value("color_method").toInt();
	product->est_ibu = query.value("est_ibu").toDouble();
	product->ibu_method = query.value("ibu_method").toInt();
	product->est_carb = query.value("est_carb").toDouble();

	product->sparge_temp = query.value("sparge_temp").toDouble();
	product->sparge_ph = query.value("sparge_ph").toDouble();
	product->sparge_volume = query.value("sparge_volume").toDouble();
	product->sparge_source = query.value("sparge_source").toInt();
	product->sparge_acid_type = query.value("sparge_acid_type").toInt();
	product->sparge_acid_perc = query.value("sparge_acid_perc").toDouble();
	product->sparge_acid_amount = query.value("sparge_acid_amount").toDouble();
	product->mash_ph = query.value("mash_ph").toDouble();
	product->mash_name = query.value("mash_name").toString();
	product->calc_acid = query.value("calc_acid").toInt() ? true:false;

	product->w1_name = query.value("w1_name").toString();
	product->w1_amount = query.value("w1_amount").toDouble();
	product->w1_calcium = query.value("w1_calcium").toDouble();
	product->w1_sulfate = query.value("w1_sulfate").toDouble();
	product->w1_chloride = query.value("w1_chloride").toDouble();
	product->w1_sodium = query.value("w1_sodium").toDouble();
	product->w1_magnesium = query.value("w1_magnesium").toDouble();
	product->w1_total_alkalinity = query.value("w1_total_alkalinity").toDouble();
	product->w1_ph = query.value("w1_ph").toDouble();
	product->w1_cost = query.value("w1_cost").toDouble();
	product->w2_name = query.value("w2_name").toString();
        product->w2_amount = query.value("w2_amount").toDouble();
        product->w2_calcium = query.value("w2_calcium").toDouble();
        product->w2_sulfate = query.value("w2_sulfate").toDouble();
        product->w2_chloride = query.value("w2_chloride").toDouble();
        product->w2_sodium = query.value("w2_sodium").toDouble();
        product->w2_magnesium = query.value("w2_magnesium").toDouble();
        product->w2_total_alkalinity = query.value("w2_total_alkalinity").toDouble();
        product->w2_ph = query.value("w2_ph").toDouble();
        product->w2_cost = query.value("w2_cost").toDouble();
	product->wg_amount = query.value("wg_amount").toDouble();
        product->wg_calcium = query.value("wg_calcium").toDouble();
        product->wg_sulfate = query.value("wg_sulfate").toDouble();
        product->wg_chloride = query.value("wg_chloride").toDouble();
        product->wg_sodium = query.value("wg_sodium").toDouble();
        product->wg_magnesium = query.value("wg_magnesium").toDouble();
        product->wg_total_alkalinity = query.value("wg_total_alkalinity").toDouble();
        product->wg_ph = query.value("wg_ph").toDouble();
	product->wb_calcium = query.value("wb_calcium").toDouble();
        product->wb_sulfate = query.value("wb_sulfate").toDouble();
        product->wb_chloride = query.value("wb_chloride").toDouble();
        product->wb_sodium = query.value("wb_sodium").toDouble();
        product->wb_magnesium = query.value("wb_magnesium").toDouble();
        product->wb_total_alkalinity = query.value("wb_total_alkalinity").toDouble();
        product->wb_ph = query.value("wb_ph").toDouble();
	product->wa_acid_name = query.value("wa_acid_name").toInt();
        product->wa_acid_perc = query.value("wa_acid_perc").toDouble();
        product->wa_base_name = query.value("wa_base_name").toInt();

	product->starter_enable = query.value("starter_enable").toInt() ? true:false;
	product->starter_type = query.value("starter_type").toInt();
	product->starter_sg = query.value("starter_sg").toDouble();
	product->starter_viability = query.value("starter_viability").toInt();
	if (query.value("yeast_prod_date").toString().length() == 10)
	    product->yeast_prod_date = query.value("yeast_prod_date").toDate();
	else
	    product->yeast_prod_date = QDate();
	product->yeast_pitchrate = query.value("yeast_pitchrate").toDouble();
	product->prop_type[0] = query.value("prop1_type").toInt();
	product->prop_volume[0] = query.value("prop1_volume").toDouble();
	product->prop_type[1] = query.value("prop2_type").toInt();
        product->prop_volume[1] = query.value("prop2_volume").toDouble();
	product->prop_type[2] = query.value("prop3_type").toInt();
        product->prop_volume[2] = query.value("prop3_volume").toDouble();
	product->prop_type[3] = query.value("prop4_type").toInt();
        product->prop_volume[3] = query.value("prop4_volume").toDouble();

	product->divide_type = query.value("divide_type").toInt();
	product->divide_size = query.value("divide_size").toDouble();
	product->divide_factor = query.value("divide_factor").toDouble();
	product->divide_parts = query.value("divide_parts").toInt();
	product->divide_part = query.value("divide_part").toInt();

	QJsonParseError parseError;
        const auto& f_json = query.value("json_fermentables").toString();
	if (!f_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(f_json);
            QJsonDocument fermentables = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
	    } else if (fermentables.isArray()) {
		double percentcheck = 0;
		for (int i = 0; i < fermentables.array().size(); i++) {
		    QJsonObject obj = fermentables.array().at(i).toObject();
		    //qDebug() << i << obj;
		    Fermentables f;
		    f.f_inventory = 0;
		    f.f_avail = false;
		    f.f_name = obj["f_name"].toString();
		    f.f_origin = obj["f_origin"].toString();
		    f.f_supplier = obj["f_supplier"].toString();
		    f.f_amount = obj["f_amount"].toDouble();
		    f.f_cost = obj["f_cost"].toDouble();
		    f.f_type = obj["f_type"].toInt();
		    f.f_yield = obj["f_yield"].toDouble();
		    f.f_color = obj["f_color"].toDouble();
		    f.f_coarse_fine_diff = obj["f_coarse_fine_diff"].toDouble();
		    f.f_moisture = obj["f_moisture"].toDouble();
		    f.f_diastatic_power = obj["f_diastatic_power"].toDouble();
		    f.f_protein = obj["f_protein"].toDouble();
		    f.f_dissolved_protein = obj["f_dissolved_protein"].toDouble();
		    f.f_max_in_batch = obj["f_max_in_batch"].toDouble();
		    f.f_graintype = obj["f_graintype"].toInt();
		    f.f_added = obj["f_added"].toInt();
		    f.f_recommend_mash = obj["f_recommend_mash"].toInt() ? true:false;
		    f.f_add_after_boil = obj["f_add_after_boil"].toInt() ? true:false;
		    f.f_adjust_to_total_100 = obj["f_adjust_to_total_100"].toInt() ? true:false;
		    f.f_percentage = obj["f_percentage"].toDouble();
		    f.f_di_ph = obj["f_di_ph"].toDouble();
		    f.f_acid_to_ph_57 = obj["f_acid_to_ph_57"].toDouble();
		    if (f.f_adjust_to_total_100)
			product->fermentables_use100 = true;
		    percentcheck += f.f_percentage;
		    /* Check and update inventory */
		    yquery.prepare("SELECT inventory FROM inventory_fermentables WHERE name=:name AND supplier=:supplier AND origin=:origin");
                    yquery.bindValue(":name", f.f_name);
                    yquery.bindValue(":supplier", f.f_supplier);
                    yquery.bindValue(":origin", f.f_origin);
                    yquery.exec();
		    if (yquery.first()) {
			f.f_avail = true;
			f.f_inventory = yquery.value(0).toDouble();
		    }
		    product->fermentables.append(f);
		}
		qDebug() << "fermentables" << product->fermentables.size() << percentcheck;
		if (percentcheck == 0 && product->fermentables.size()) {
		    /* Missing percentages, fix it. */
		    double total = 0;
		    for (int i = 0; i < product->fermentables.size(); i++) {
			if (product->fermentables.at(i).f_added < 4)
			    total += product->fermentables.at(i).f_amount;
		    }
		    for (int i = 0; i < product->fermentables.size(); i++) {
			if (product->fermentables.at(i).f_added < 4)
			    product->fermentables[i].f_percentage = round((product->fermentables.at(i).f_amount / total) * 10000.0) / 100.0;
		    }
		    qDebug() << " fixed missing percentages";
		    is_changed();
		}
	    }
        } else {
	    qDebug() << "empty fermentables";
	}

	const auto& h_json = query.value("json_hops").toString();
        if (!h_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(h_json);
            QJsonDocument hops = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
	    } else if (hops.isArray()) {
		for (int i = 0; i < hops.array().size(); i++) {
		    QJsonObject obj = hops.array().at(i).toObject();
		    //qDebug() << i << obj;
		    Hops h;
		    h.h_avail = false;
		    h.h_inventory = 0;
		    h.h_name = obj["h_name"].toString();
		    h.h_origin = obj["h_origin"].toString();
		    h.h_amount = obj["h_amount"].toDouble();
		    h.h_cost = obj["h_cost"].toDouble();
		    h.h_type = obj["h_type"].toInt();
		    h.h_form = obj["h_form"].toInt();
		    h.h_useat = obj["h_useat"].toInt();
		    h.h_time = obj["h_time"].toInt();
		    h.h_alpha = obj["h_alpha"].toDouble();
		    h.h_beta = obj["h_beta"].toDouble();
		    h.h_hsi = obj["h_hsi"].toDouble();
		    h.h_humulene = obj["h_humulene"].toDouble();
		    h.h_caryophyllene = obj["h_caryophyllene"].toDouble();
		    h.h_cohumulone = obj["h_cohumulone"].toDouble();
		    h.h_myrcene = obj["h_myrcene"].toDouble();
		    h.h_total_oil = obj["h_total_oil"].toDouble();
		    /* Check and update inventory */
                    yquery.prepare("SELECT inventory FROM inventory_hops WHERE name=:name AND origin=:origin");
                    yquery.bindValue(":name", h.h_name);
                    yquery.bindValue(":origin", h.h_origin);
                    yquery.exec();
                    if (yquery.first()) {
                        h.h_avail = true;
                        h.h_inventory = yquery.value(0).toDouble();
                    }
		    product->hops.append(h);
		}
		qDebug() << "hops" << product->hops.size();
	    }
        } else {
            qDebug() << "empty hops";
        }

	const auto& m_json = query.value("json_miscs").toString();
        if (!m_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(m_json);
            QJsonDocument miscs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
	    } else if (miscs.isArray()) {
		for (int i = 0; i < miscs.array().size(); i++) {
		    QJsonObject obj = miscs.array().at(i).toObject();
		    //qDebug() << i << obj;
		    Miscs m;
		    m.m_avail = false;
		    m.m_inventory = 0;
		    m.m_name = obj["m_name"].toString();
		    m.m_amount = obj["m_amount"].toDouble();
		    m.m_type = obj["m_type"].toInt();
		    m.m_use_use = obj["m_use_use"].toInt();
		    m.m_time = obj["m_time"].toDouble();
		    m.m_amount_is_weight = obj["m_amount_is_weight"].toInt() ? true:false;
		    m.m_cost = obj["m_cost"].toDouble();
		    /* Check and update inventory */
                    yquery.prepare("SELECT inventory FROM inventory_miscs WHERE name=:name");
                    yquery.bindValue(":name", m.m_name);
                    yquery.exec();
                    if (yquery.first()) {
                        m.m_avail = true;
                        m.m_inventory = yquery.value(0).toDouble();
                    }
		    product->miscs.append(m);
		}
		qDebug() << "miscs" << product->miscs.size();
	    }
        } else {
            qDebug() << "empty miscs";
        }

	const auto& y_json = query.value("json_yeasts").toString();
        if (!y_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(y_json);
            QJsonDocument yeasts = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
	    } else if (yeasts.isArray()) {
		for (int i = 0; i < yeasts.array().size(); i++) {
		    QJsonObject obj = yeasts.array().at(i).toObject();
		    //qDebug() << i << obj;
		    Yeasts y;
		    /* First some defaults for newer fields. */
		    y.y_tolerance = y.y_harvest_time = y.y_pitch_temperature = y.y_zymocide = 0;
                    y.y_avail = y.y_sta1 = y.y_bacteria = y.y_harvest_top = y.y_pofpos = false;
		    y.y_gr_hl_lo = 50;
                    y.y_sg_lo = 1.04;
                    y.y_gr_hl_hi = 80;
                    y.y_sg_hi = 1.08;
		    /* Now get what we have got */
		    y.y_name = obj["y_name"].toString();
		    y.y_laboratory = obj["y_laboratory"].toString();
		    y.y_product_id = obj["y_product_id"].toString();
		    y.y_amount = obj["y_amount"].toDouble();
		    y.y_type = obj["y_type"].toInt();
		    y.y_form = obj["y_form"].toInt();
		    y.y_min_temperature = obj["y_min_temperature"].toDouble();
		    y.y_max_temperature = obj["y_max_temperature"].toDouble();
		    y.y_flocculation = obj["y_flocculation"].toInt();
		    y.y_attenuation = obj["y_attenuation"].toDouble();
		    y.y_cells = obj["y_cells"].toDouble();
		    y.y_tolerance = obj["y_tolerance"].toDouble();
		    y.y_inventory = obj["y_inventory"].toDouble();
		    y.y_use = obj["y_use"].toInt();
		    y.y_sta1 = obj["y_sta1"].toInt() ? true:false;
		    y.y_bacteria = obj["y_bacteria"].toInt() ? true:false;
		    y.y_harvest_top = obj["y_harvest_top"].toInt() ? true:false;
		    y.y_harvest_time = obj["y_harvest_time"].toInt();
		    y.y_pitch_temperature = obj["y_pitch_temperature"].toDouble();
		    y.y_pofpos = obj["y_pofpos"].toInt() ? true:false;
		    y.y_zymocide = obj["y_zymocide"].toInt();
		    y.y_gr_hl_lo = obj["y_gr_hl_lo"].toInt();
		    y.y_sg_lo = obj["y_sg_lo"].toDouble();
		    y.y_gr_hl_hi = obj["y_gr_hl_hi"].toInt();
		    y.y_sg_hi = obj["y_sg_hi"].toDouble();
		    y.y_cost = obj["y_cost"].toDouble();
		    /*
		     * Possible data upgrade needed.
		     */
		    yquery.prepare("SELECT tolerance,cells,sta1,bacteria,harvest_top,harvest_time,pitch_temperature,"
				       "pofpos,zymocide,gr_hl_lo,sg_lo,gr_hl_hi,sg_hi,inventory "
				       "FROM inventory_yeasts WHERE name=:name AND laboratory=:laboratory AND product_id=:product_id");
		    yquery.bindValue(":name", y.y_name);
		    yquery.bindValue(":laboratory", y.y_laboratory);
		    yquery.bindValue(":product_id", y.y_product_id);
            	    yquery.exec();
            	    if (yquery.first()) {
			if (y.y_tolerance == 0 || y.y_cells == 0) {
			    y.y_tolerance = yquery.value(0).toDouble();
			    y.y_cells = yquery.value(1).toDouble();
			}
			y.y_sta1 = yquery.value(2).toInt() ? true:false;
			y.y_bacteria = yquery.value(3).toInt() ? true:false;
			y.y_harvest_top = yquery.value(4).toInt() ? true:false;
			y.y_harvest_time = yquery.value(5).toInt();
			y.y_pitch_temperature = yquery.value(6).toDouble();
			y.y_pofpos = yquery.value(7).toInt() ? true:false;
			y.y_zymocide = yquery.value(8).toInt();
			y.y_gr_hl_lo = yquery.value(9).toInt();
			y.y_sg_lo = yquery.value(10).toDouble();
			y.y_gr_hl_hi = yquery.value(11).toInt();
			y.y_sg_hi = yquery.value(12).toDouble();
			y.y_inventory = yquery.value(13).toDouble();
			y.y_avail = true;
		    } else {
			qDebug() << y.y_name << y.y_product_id << "not found for upgrade";
		    }
		    product->yeasts.append(y);
		}
		qDebug() << "yeasts" << product->yeasts.size();
	    }
        } else {
            qDebug() << "empty yeasts";
        }

	const auto& ma_json = query.value("json_mashs").toString().trimmed();
        if (!ma_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(ma_json);
            QJsonDocument mashs = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
	    } else if (mashs.isArray()) {
        	for (int i = 0; i < mashs.array().size(); i++) {
            	    QJsonObject obj = mashs.array().at(i).toObject();
		    qDebug() << i << obj;
		    Mashs m;
		    m.step_name = obj["step_name"].toString();
		    m.step_type = obj["step_type"].toInt();
		    m.step_volume = obj["step_volume"].toDouble();
		    m.step_infuse_amount = obj["step_infuse_amount"].toDouble();
		    m.step_infuse_temp = obj["step_infuse_temp"].toDouble();
		    m.step_temp = obj["step_temp"].toDouble();
		    m.step_time = obj["step_time"].toDouble();
		    m.ramp_time = obj["ramp_time"].toDouble();
		    m.end_temp = obj["end_temp"].toDouble();
		    m.step_wg_ratio = obj["step_wg_ratio"].toDouble();
		    m.step_ph = obj["step_ph"].toDouble();
		    m.step_sg = obj["step_sg"].toDouble();
		    product->mashs.append(m);
		}
	    }
        } else {
            qDebug() << "empty mashs";
        }
	qDebug() << "mashs" << product->mashs.size();

    } else {
	/* New product, set some defaults */
	product->locked = product->log_brew = product->log_fermentation = product->log_ispindel = product->log_co2pressure = false;
	product->st_name = "";
	product->st_letter = "";
	product->st_guide = "";
	product->st_category = "";
	product->st_category_number = 0;
	product->st_type = 0;
	product->st_og_min = 1.025; product->st_og_max = 1.100;
	product->st_fg_min = 1.000; product->st_fg_max = 1.020;
	product->st_ibu_min = 5; product->st_ibu_max = 200;
	product->st_color_min = 3; product->st_color_max = 100;
	product->st_carb_min = 1.0; product->st_carb_max = 4.5;
	product->st_abv_min = 1; product->st_abv_max = 15;
	product->eq_name = product->eq_notes = product->name = product->code = "";
	product->eq_tun_specific_heat = 0.11;
	product->eq_tun_material = 0;
	product->eq_tun_volume = product->eq_tun_height = 20;
	product->eq_tun_weight = 2;
	product->eq_top_up_water = 0;
	product->eq_trub_chiller_loss = 0.5;
	product->eq_evap_rate = 1.8;
	product->eq_calc_boil_volume = true;
	product->eq_top_up_kettle = 0;
	product->eq_hop_utilization = 100;
	product->eq_lauter_volume = product->eq_lauter_height = product->eq_kettle_volume = product->eq_kettle_height = product->eq_mash_volume = 20;
	product->eq_lauter_deadspace = 0.5;
	product->eq_mash_max = 6;
	product->eq_efficiency = 75;
	product->birth = QDate::currentDate();
	product->stage = product->inventory_reduced = PROD_STAGE_PLAN;
	product->notes = "";
	product->efficiency = 75;
	product->eq_batch_size = product->batch_size = 20;
	product->eq_boil_time = product->boil_time = 60;
	product->eq_boil_size = product->boil_size = product->batch_size + (round(product->batch_size * product->boil_time / 60.0) / 10.0);
	product->type = 2;
	product->est_og = product->est_og3 = product->est_fg = product->est_color = product->est_ibu = product->est_abv = 0;
	product->brew_date_start = product->brew_date_end = QDateTime();
	product->brew_mash_ph = product->brew_mash_sg = product->brew_mash_efficiency = 0;
	product->brew_sparge_temperature = product->brew_sparge_volume = product->brew_sparge_est = product->brew_sparge_ph = 0;
	product->brew_preboil_volume = product->brew_preboil_sg = product->brew_preboil_ph = product->brew_preboil_efficiency = 0;
	product->brew_aboil_volume = product->brew_aboil_sg = product->brew_aboil_ph = product->brew_aboil_efficiency = 0;
	product->brew_cooling_method = product->brew_cooling_time = 0;
	product->brew_cooling_to = 20;
	product->brew_whirlpool9 = product->brew_whirlpool7 = product->brew_whirlpool6 = product->brew_whirlpool2 = 0;
	product->brew_fermenter_volume = product->brew_fermenter_extrawater = product->brew_fermenter_tcloss = 0;
	product->brew_aeration_time = product->brew_aeration_speed = product->brew_aeration_type = 0;
	product->brew_fermenter_sg = product->brew_fermenter_ibu = product->brew_fermenter_color = 0;
	product->og = product->fg = 0;
	product->primary_start_temp = product->primary_max_temp = product->primary_end_temp = product->primary_end_sg = 0;
	product->primary_end_date = product->secondary_end_date = QDate();
	product->secondary_temp = product->secondary_end_sg = product->tertiary_temp = 0;
	product->package_date = QDate();
	product->package_volume = product->package_infuse_amount = product->package_infuse_abv = product->package_abv = product->package_ph = 0;
	product->package_infuse_notes = "";
	product->bottle_amount = product->bottle_carbonation = product->bottle_priming_amount = product->bottle_carbonation_temp = 0;
	product->keg_amount = product->keg_carbonation = product->keg_priming_amount = product->keg_priming_water = 0;
	product->keg_carbonation_temp = product->keg_pressure = 0;
	product->bottle_priming_water = product->keg_priming_sugar = 0;
	product->taste_rate = 0;
	product->taste_date = QDate();
	product->taste_notes = product->taste_color = product->taste_transparency = product->taste_head = "";
	product->taste_aroma = product->taste_taste = product->taste_mouthfeel = product->taste_aftertaste = "";
	product->sparge_temp = 80;
        product->sparge_ph = 5.4;
        product->sparge_volume = 8;
        product->sparge_source = 0;
        product->sparge_acid_type = 0;
        product->sparge_acid_perc = 80;
        product->sparge_acid_amount = 0;
        product->mash_ph = 5.4;
        product->mash_name = "";
        product->calc_acid = true;
	product->w1_name = "";
        product->w1_amount = 0;
        product->w1_calcium = 0;
        product->w1_sulfate = 0;
        product->w1_chloride = 0;
        product->w1_sodium = 0;
        product->w1_magnesium = 0;
        product->w1_total_alkalinity = 0;
        product->w1_ph = 7;
        product->w1_cost = 0;
        product->w2_name = "";
        product->w2_amount = 0;
        product->w2_calcium = 0;
        product->w2_sulfate = 0;
        product->w2_chloride = 0;
        product->w2_sodium = 0;
        product->w2_magnesium = 0;
        product->w2_total_alkalinity = 0;
        product->w2_ph = 7;
        product->w2_cost = 0;
        product->wg_amount = 0;
        product->wg_calcium = 0;
        product->wg_sulfate = 0;
        product->wg_chloride = 0;
        product->wg_sodium = 0;
        product->wg_magnesium = 0;
        product->wg_total_alkalinity = 0;
        product->wg_ph = 7;
        product->wb_calcium = 0;
        product->wb_sulfate = 0;
        product->wb_chloride = 0;
        product->wb_sodium = 0;
        product->wb_magnesium = 0;
        product->wb_total_alkalinity = 0;
        product->wb_ph = 7;
	product->wa_acid_name = 0;
	product->wa_acid_perc = 80;
	product->wa_base_name = 0;
	product->starter_enable = false;
	product->starter_type = product->prop_type[0] = product->prop_type[1] = product->prop_type[2] = product->prop_type[3] = 0;
	product->starter_viability = 100;
	product->starter_sg = 1.037;
	product->yeast_prod_date = QDate();
	product->yeast_pitchrate = product->prop_volume[0] = product->prop_volume[1] = product->prop_volume[2] = product->prop_volume[3] = 0;
	product->divide_type = product->divide_parts = product->divide_part = 0;
	product->divide_size = product->divide_factor = 0;
    }

    // Tab generic.
    ui->lockedEdit->setChecked(product->locked);
    ui->st_nameEdit->setText(product->st_name);
    ui->st_groupEdit->setText(product->st_letter);
    ui->st_guideEdit->setText(product->st_guide);
    ui->st_catEdit->setText(product->st_category);
    ui->st_catnrEdit->setText(QString("%1").arg(product->st_category_number));
    ui->st_typeEdit->setText(style_types[product->st_type]);
    ui->nameEdit->setText(product->name);
    ui->codeEdit->setText(product->code);
    ui->birthEdit->setText(product->birth.toString("dd MMM yyyy"));
    ui->notesEdit->setPlainText(product->notes);
    ui->typeEdit->setCurrentIndex(product->type);
    ui->batch_sizeEdit->setValue(product->batch_size);
    ui->boil_sizeEdit->setValue(product->boil_size);
    ui->boil_timeEdit->setValue(product->boil_time);
    ui->efficiencyEdit->setValue(product->efficiency);
    if (product->divide_type > 0)
	ui->splitatEdit->setText(QString(tr("%1, part %2 of %3").arg(prod_split[product->divide_type])
								.arg(product->divide_part + 1)
								.arg(product->divide_parts + 1)));
    else
        ui->splitatEdit->setText(prod_split[product->divide_type]);
    ui->est_ogEdit->setValue(product->est_og);
    ui->est_ogShow->setRange(product->st_og_min, product->st_og_max);
    ui->est_ogShow->setPrecision(3);
    ui->est_ogShow->setMarkerTextIsValue(true);
    ui->est_ogShow->setValue(product->est_og);
    ui->est_fgEdit->setValue(product->est_fg);
    ui->est_fgShow->setRange(product->st_fg_min, product->st_fg_max);
    ui->est_fgShow->setPrecision(3);
    ui->est_fgShow->setMarkerTextIsValue(true);
    ui->est_fgShow->setValue(product->est_fg);
    ui->est_abvEdit->setValue(product->est_abv);
    ui->est_abvShow->setRange(product->st_abv_min, product->st_abv_max);
    ui->est_abvShow->setPrecision(1);
    ui->est_abvShow->setMarkerTextIsValue(true);
    ui->est_abvShow->setValue(product->est_abv);
    ui->est_colorEdit->setValue(product->est_color);
    ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(product->est_color));
    ui->est_colorShow->setPrecision(0);
    ui->est_colorShow->setMarkerTextIsValue(true);
    ui->est_colorShow->setRange(product->st_color_min, product->st_color_max);
    ui->est_colorShow->setValue(product->est_color);
    ui->color_methodEdit->setCurrentIndex(product->color_method);
    ui->est_ibuEdit->setValue(product->est_ibu);
    ui->est_ibuShow->setPrecision(0);
    ui->est_ibuShow->setMarkerTextIsValue(true);
    ui->est_ibuShow->setRange(product->st_ibu_min, product->st_ibu_max);
    ui->est_ibuShow->setValue(product->est_ibu);
    ui->ibu_methodEdit->setCurrentIndex(product->ibu_method);
    ui->est_carbEdit->setValue(product->est_carb);
    ui->est_carbShow->setPrecision(1);
    ui->est_carbShow->setMarkerTextIsValue(true);
    ui->est_carbShow->setRange(product->st_carb_min, product->st_carb_max);
    ui->est_carbShow->setValue(product->est_carb);

    // Tab equipment.
    initEquipment();

    // Tab fermentables.
    ui->est_og2Edit->setValue(product->est_og);
    ui->est_color2Edit->setValue(product->est_color);
    ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(product->est_color));

    // Tab hops.
    ui->est_ibu2Edit->setValue(product->est_ibu);

    // Tab yeasts.
    initYeast();

    // Tab mashs.
    ui->mash_nameEdit->setText(product->mash_name);

    // Tab waters.
    qDebug() << "water 1" << product->w1_name << "default" << my_default_water;
    if (product->w1_ph > 4.0) {
	/*
	 * Water data seems present, use that and set the name between []
	 */
	ui->w1_nameEdit->setPlaceholderText(QString("["+product->w1_name+"]"));
    } else {
	bool found = false;
	if (product->w1_name != "") {
	    /*
	     * We have a name, but do we know it?
	     */
	    query.prepare("SELECT * FROM inventory_waters WHERE name=:water");
            query.bindValue(":water", product->w1_name);
	    query.exec();
	    found = query.first();
	}
	if (!found) {
	    /*
	     * Try to load default water
	     */
	    query.prepare("SELECT * FROM inventory_waters WHERE record=:record");
	    query.bindValue(":record", my_default_water);
	    query.exec();
	    found = query.first();
	}
	if (found) {
	    product->w1_calcium = query.value(3).toDouble();
	    product->w1_magnesium = query.value(8).toDouble();
	    product->w1_total_alkalinity = query.value(11).toDouble();
	    product->w1_sodium = query.value(7).toDouble();
	    product->w1_chloride = query.value(6).toDouble();
	    product->w1_sulfate = query.value(5).toDouble();
	    product->w1_ph = query.value(9).toDouble();
            ui->w1_nameEdit->setCurrentIndex(query.value(0).toInt());
	} else {
	    product->w1_calcium = 0;
	    product->w1_magnesium = 0;
	    product->w1_total_alkalinity = 0;
	    product->w1_sodium = 0;
	    product->w1_chloride = 0;
	    product->w1_sulfate = 0;
	    product->w1_ph = 0;
	}
    }
    ui->w1_volEdit->setValue(product->w1_amount);
    ui->w1_caEdit->setValue(product->w1_calcium);
    ui->w1_mgEdit->setValue(product->w1_magnesium);
    ui->w1_hco3Edit->setValue(product->w1_total_alkalinity * 1.22);
    ui->w1_caco3Edit->setValue(product->w1_total_alkalinity);
    ui->w1_naEdit->setValue(product->w1_sodium);
    ui->w1_clEdit->setValue(product->w1_chloride);
    ui->w1_so4Edit->setValue(product->w1_sulfate);
    ui->w1_phEdit->setValue(product->w1_ph);

    qDebug() << "water 2" << product->w2_name;
    if (product->w2_ph  > 4.0) {
	ui->w2_nameEdit->setPlaceholderText(QString("["+product->w2_name+"]"));
    } else if (product->w2_name != "") {
	query.prepare("SELECT * FROM inventory_waters WHERE name=:water");
	query.bindValue(":water", product->w2_name);
	query.exec();
	if (query.first()) {
	    product->w2_calcium = query.value(3).toDouble();
            product->w2_magnesium = query.value(8).toDouble();
            product->w2_total_alkalinity = query.value(11).toDouble();
            product->w2_sodium = query.value(7).toDouble();
            product->w2_chloride = query.value(6).toDouble();
            product->w2_sulfate = query.value(5).toDouble();
            product->w2_ph = query.value(9).toDouble();
	    ui->w2_nameEdit->setCurrentIndex(query.value(0).toInt());
	} else {
	    product->w2_calcium = 0;
            product->w2_magnesium = 0;
            product->w2_total_alkalinity = 0;
            product->w2_sodium = 0;
            product->w2_chloride = 0;
            product->w2_sulfate = 0;
            product->w2_ph = 0;
	}
    }
    ui->w2_volEdit->setValue(product->w2_amount);
    ui->w2_caEdit->setValue(product->w2_calcium);
    ui->w2_mgEdit->setValue(product->w2_magnesium);
    ui->w2_hco3Edit->setValue(product->w2_total_alkalinity * 1.22);
    ui->w2_caco3Edit->setValue(product->w2_total_alkalinity);
    ui->w2_naEdit->setValue(product->w2_sodium);
    ui->w2_clEdit->setValue(product->w2_chloride);
    ui->w2_so4Edit->setValue(product->w2_sulfate);
    ui->w2_phEdit->setValue(product->w2_ph);
    ui->mw_autoEdit->setChecked(product->calc_acid);
    ui->mw_phEdit->setReadOnly(! product->calc_acid);
    ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
    ui->mw_acidvolEdit->setReadOnly(product->calc_acid);
    ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);

    ui->sp_volEdit->setValue(product->sparge_volume);
    ui->sp_tempEdit->setValue(product->sparge_temp);
    ui->sp_phEdit->setValue(product->sparge_ph);
    ui->sp_sourceEdit->setCurrentIndex(product->sparge_source);
    ui->sp_acidtypeEdit->setCurrentIndex(product->sparge_acid_type);
    ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
    ui->sp_acidvolEdit->setValue(product->sparge_acid_amount);

    // Tab brewday.
    updateBrewday();
    ui->brew_mashphEdit->setValue(product->brew_mash_ph);
    ui->brew_mashphShow->setValue(product->mash_ph);
    ui->brew_mashsgEdit->setValue(product->brew_mash_sg);
    ui->brew_mashsgShow->setValue(0);
    ui->brew_masheffShow->setValue(product->brew_mash_efficiency);
    ui->brew_spargetempShow->setValue(product->sparge_temp);
    ui->brew_spargevolShow->setValue(product->sparge_volume);
    ui->brew_spargeestShow->setValue(product->brew_sparge_est);
    ui->brew_spargephEdit->setValue(product->brew_sparge_ph);
    ui->brew_spargephShow->setValue(product->sparge_ph);

    ui->brew_preboilphEdit->setValue(product->brew_preboil_ph);
    ui->brew_preboilsgEdit->setValue(product->brew_preboil_sg);
    ui->brew_preboilvolEdit->setValue(product->brew_preboil_volume);
    ui->brew_preboilvolShow->setValue(product->boil_size * 1.04);
    ui->brew_preboileffShow->setValue(product->brew_preboil_efficiency);
    calcEfficiencyBeforeBoil();
    ui->brew_aboilphEdit->setValue(product->brew_aboil_ph);
    ui->brew_aboilsgEdit->setValue(product->brew_aboil_sg);
    ui->brew_aboilvolEdit->setValue(product->brew_aboil_volume);
    ui->brew_aboilvolShow->setValue(product->batch_size * 1.04);
    ui->brew_aboileffShow->setValue(product->brew_aboil_efficiency);
    calcEfficiencyAfterBoil();
    ui->brew_whirlpool9Edit->setValue(product->brew_whirlpool9);
    ui->brew_whirlpool7Edit->setValue(product->brew_whirlpool7);
    ui->brew_whirlpool6Edit->setValue(product->brew_whirlpool6);
    ui->brew_whirlpool2Edit->setValue(product->brew_whirlpool2);
    ui->brew_cooltoEdit->setValue(product->brew_cooling_to);
    ui->brew_coolwithEdit->setCurrentIndex(product->brew_cooling_method);
    ui->brew_cooltimeEdit->setValue(product->brew_cooling_time);

    ui->brew_trublossEdit->setValue(product->brew_fermenter_tcloss);
    ui->brew_topupwaterEdit->setValue(product->brew_fermenter_extrawater);
    ui->brew_tofermentEdit->setValue(product->brew_fermenter_volume);
    ui->brew_fermentsgShow->setValue(product->brew_fermenter_sg);
    ui->brew_fermentcolorShow->setValue(product->brew_fermenter_color);
    ui->brew_fermentcolorShow->setStyleSheet(Utils::ebc_to_style(product->brew_fermenter_color));
    ui->brew_fermentibuShow->setValue(product->brew_fermenter_ibu);

    ui->brew_aerwithEdit->setCurrentIndex(product->brew_aeration_type);
    ui->brew_aertimeEdit->setValue(product->brew_aeration_time);
    ui->brew_aerspeedEdit->setValue(product->brew_aeration_speed);

    // Tab fermentation.
    ui->prim_ogShow->setValue(product->brew_fermenter_sg);
    ui->prim_startCEdit->setValue(product->primary_start_temp);
    ui->prim_maxCEdit->setValue(product->primary_max_temp);
    ui->prim_endCEdit->setValue(product->primary_end_temp);
    ui->prim_endsgEdit->setValue(product->primary_end_sg);
    ui->prim_enddateEdit->setText(product->primary_end_date.toString("dd MMM yyyy"));
    ui->prim_attShow->setValue(Utils::calc_svg(product->brew_fermenter_sg, product->primary_end_sg));
    ui->sec_tempEdit->setValue(product->secondary_temp);
    ui->sec_sgEdit->setValue(product->secondary_end_sg);
    ui->sec_enddateEdit->setText(product->secondary_end_date.toString("dd MMM yyyy"));
    ui->sec_attShow->setValue(Utils::calc_svg(product->brew_fermenter_sg, product->secondary_end_sg));
    ui->tert_tempEdit->setValue(product->tertiary_temp);
    ui->tert_estsgShow->setValue(product->est_fg);
    ui->tert_sgEdit->setValue(product->fg);
    ui->tert_attShow->setValue(Utils::calc_svg(product->brew_fermenter_sg, product->fg));
    ui->tert_abvShow->setValue(Utils::abvol(product->brew_fermenter_sg, product->fg));

    // Tab packaging.
    ui->pack_dateEdit->setText(product->package_date.toString("dd MMM yyyy"));
    ui->pack_carbloShow->setValue(product->st_carb_min);
    ui->pack_carbhiShow->setValue(product->st_carb_max);
    ui->pack_volumeEdit->setValue(product->package_volume);
    ui->pack_addvolEdit->setValue(product->package_infuse_amount);
    ui->pack_addabvEdit->setValue(product->package_infuse_abv);
    ui->pack_notesEdit->setText(product->package_infuse_notes);
    ui->pack_abvShow->setValue(product->package_abv);
    ui->pack_phEdit->setValue(product->package_ph);

    ui->bottle_volumeEdit->setValue(product->bottle_amount);
    ui->bottle_carbEdit->setValue(product->bottle_carbonation);
    ui->bottle_sug_amountShow->setValue(product->bottle_priming_amount);
    ui->bottle_sug_waterEdit->setValue(product->bottle_priming_water);
    ui->bottle_tempEdit->setValue(product->bottle_carbonation_temp);

    // Tab taste.
    ui->taste_dateEdit->setText(product->taste_date.toString("dd MMM yyyy"));
    ui->taste_rateEdit->setValue(product->taste_rate);
    ui->taste_notesEdit->setPlainText(product->taste_notes);
    ui->taste_colorEdit->setText(product->taste_color);
    ui->taste_transparencyEdit->setText(product->taste_transparency);
    ui->taste_headEdit->setText(product->taste_head);
    ui->taste_aromaEdit->setText(product->taste_aroma);
    ui->taste_tasteEdit->setText(product->taste_taste);
    ui->taste_mouthfeelEdit->setText(product->taste_mouthfeel);
    ui->taste_aftertasteEdit->setText(product->taste_aftertaste);

    // All signals from tab "Generic"
    connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditProduct::is_changed);
    connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditProduct::name_changed);
    connect(ui->notesEdit, SIGNAL(textChanged()), this, SLOT(notes_changed()));
    connect(ui->typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::brew_type_changed);
    connect(ui->batch_sizeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::batch_size_changed);
    connect(ui->boil_timeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::boil_time_changed);
    connect(ui->efficiencyEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::efficiency_changed);
    connect(ui->beerstyleEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::style_changed);
    connect(ui->est_ogEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::est_og_changed);
    connect(ui->color_methodEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::colormethod_changed);
    connect(ui->ibu_methodEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ibumethod_changed);
    connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditProduct::locked_changed);

    // All signals from tab "Fermentables"
    ui->fermentablesTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(ui->est_og2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::est_og_changed);
    connect(ui->perc_mashShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_mash_valueChanged);
    connect(ui->perc_sugarsShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_sugars_valueChanged);
    connect(ui->perc_caraShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_cara_valueChanged);
    connect(ui->lintnerShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_lintner_valueChanged);
    connect(ui->addFermentable, SIGNAL(clicked()), this, SLOT(addFermentRow_clicked()));

    // All signals from tab "Hops"
    ui->hopsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(ui->hop_tasteShow, &QProgressBar::valueChanged, this, &EditProduct::hop_Flavour_valueChanged);
    connect(ui->hop_aromaShow, &QProgressBar::valueChanged, this, &EditProduct::hop_Aroma_valueChanged);
    connect(ui->addHop, SIGNAL(clicked()), this, SLOT(addHopRow_clicked()));

    // All signals from tab "Miscs"
    ui->miscsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(ui->addMisc, SIGNAL(clicked()), this, SLOT(addMiscRow_clicked()));

    // All signals from tab "Yeasts"
    connect(ui->addYeast, SIGNAL(clicked()), this, SLOT(addYeastRow_clicked()));
    connect(ui->stmethodEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_method_changed);
    connect(ui->startersgEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::yeast_starter_sg_changed);
    connect(ui->productionButton1, SIGNAL(clicked()), this, SLOT(yeast_prod_date_today()));
    connect(ui->productionButton2, SIGNAL(clicked()), this, SLOT(yeast_prod_date_clear()));
    connect(ui->productionEdit, &QDateEdit::dateChanged, this, &EditProduct::yeast_prod_date_changed);

    // All signals from tab "Mash"
    ui->mashsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(ui->addMash, SIGNAL(clicked()), this, SLOT(addMashRow_clicked()));
    connect(ui->mash_pickEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::mash_select_changed);
    connect(ui->mash_nameEdit, &QLineEdit::textChanged, this, &EditProduct::mash_name_changed);

    // All signals from tab "Water"
    connect(ui->bs_cacl2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_cacl2_changed);
    connect(ui->bs_caso4Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_caso4_changed);
    connect(ui->bs_mgso4Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_mgso4_changed);
    connect(ui->bs_naclEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_nacl_changed);
    connect(ui->bs_mgcl2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_mgcl2_changed);
    connect(ui->bs_nahco3Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_nahco3_changed);
    connect(ui->bs_caco3Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_caco3_changed);
    connect(ui->mw_autoEdit, &QCheckBox::stateChanged, this, &EditProduct::mw_calc_acid_clicked);
    connect(ui->mw_phEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::mw_ph_changed);
    connect(ui->mw_acidvolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::mw_acid_changed);
    connect(ui->mw_acidPick, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::mw_type_changed);
    connect(ui->wt_sourceEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::wt_target_changed);
    connect(ui->w1_nameEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::w1_name_changed);
    connect(ui->w2_nameEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::w2_name_changed);
    connect(ui->w2_volEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::w2_volume_changed);
    connect(ui->sp_sourceEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::sp_source_changed);
    connect(ui->sp_acidtypeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::sp_type_changed);
    connect(ui->sp_phEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::sp_ph_changed);

    /* All signals from tab Brewday */
    connect(ui->brew_startButton1, SIGNAL(clicked()), this, SLOT(brew_date_today()));
    connect(ui->brew_startButton2, SIGNAL(clicked()), this, SLOT(brew_date_clear()));
    connect(ui->brew_startDate, &QDateEdit::dateChanged, this, &EditProduct::brew_start_date_changed);
    connect(ui->brew_mashphEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_mashph_changed);
    connect(ui->brew_mashsgEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_mashsg_changed);
    connect(ui->brew_spargephEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_spargeph_changed);
    connect(ui->brew_preboilphEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_preboilph_changed);
    connect(ui->brew_preboilsgEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_preboilsg_changed);
    connect(ui->brew_preboilvolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_preboilvol_changed);
    connect(ui->brew_aboilphEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_aboilph_changed);
    connect(ui->brew_aboilsgEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_aboilsg_changed);
    connect(ui->brew_aboilvolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_aboilvol_changed);
    connect(ui->brew_coolwithEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::brew_cooling_method_changed);
    connect(ui->brew_cooltoEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_cooling_to_changed);
    connect(ui->brew_cooltimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_cooling_time_changed);
    connect(ui->brew_whirlpool9Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_whirlpool9_changed);
    connect(ui->brew_whirlpool7Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_whirlpool7_changed);
    connect(ui->brew_whirlpool6Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_whirlpool6_changed);
    connect(ui->brew_whirlpool2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_whirlpool2_changed);
    connect(ui->brew_aerwithEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::brew_aerwith_changed);
    connect(ui->brew_aerspeedEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_aerspeed_changed);
    connect(ui->brew_aertimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_aertime_changed);
    connect(ui->brew_trublossEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_trubloss_changed);
    connect(ui->brew_topupwaterEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::brew_topupwater_changed);

    setStage();

    ui->saveButton->setEnabled(false);
    ui->deleteButton->setEnabled((id >= 0 && ! product->locked) ? true:false);

    emit refreshAll();
}


EditProduct::~EditProduct()
{
    qDebug() << "EditProduct done start";
    delete ui;
    emit entry_changed();
    qDebug() << "EditProduct done final";
}


void EditProduct::calcSupplies()
{
    if (product->inventory_reduced > PROD_STAGE_PACKAGE) {
	ui->ok_pmptLabel->setVisible(false);
	ui->ok_pmptIcon->setVisible(false);
	return;
    }

    qDebug() << "calcSupplies() f:" << product->fermentables_ok << "h:" << product->hops_ok << "m:" << product->miscs_ok << "y:" << product->yeasts_ok /*<< "w:" << product->waters_ok*/;
    if (product->fermentables_ok && product->hops_ok && product->miscs_ok && product->yeasts_ok /*&& product->waters_ok */) {
	ui->ok_pmptIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/silk/tick.png")));
    } else {
	ui->ok_pmptIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/silk/cancel.png")));
    }
}


void EditProduct::refreshAll()
{
    refreshFermentables();
    calcFermentables();		/* Must be before Hops */
    refreshHops();
    calcIBUs();
    refreshMiscs();
    calcMiscs();
    refreshYeasts();
    calcYeast();
    calcMash();
    refreshMashs();
    refreshWaters();
    calcWater();
    calcSupplies();
}


/*
 * Window header, mark any change with '**'
 */
void EditProduct::WindowTitle()
{
    QString txt;

    if (this->recno < 0) {
	txt = QString(tr("BMSapp - Add new product"));
    } else {
	txt = QString(tr("BMSapp - Edit product %1").arg(this->recno));
    }

    if (this->textIsChanged) {
	txt.append((QString(" **")));
    }
    setWindowTitle(txt);
}


void EditProduct::on_saveButton_clicked()
{
    QSqlQuery query;

    /* If there are errors in the form, show a message and do "return;" */
    if (ui->nameEdit->text().length() < 2) {
	QMessageBox::warning(this, tr("Edit Product"), tr("Name empty or too short."));
	return;
    }
    if (ui->st_nameEdit->text().length() < 2) {
        QMessageBox::warning(this, tr("Edit Product"), tr("No beerstyle selected."));
        return;
    }

    if (this->textIsChanged) {
    	if (this->recno == -1) {
    	    query.prepare("INSERT INTO products SET "
		"name=:name, code=:code, birth=:birth, stage=:stage, notes=:notes, log_brew=:log_brew, "
		"log_fermentation=:log_fermentation, log_ispindel=:log_ispindel, log_co2pressure=:log_co2pressure, "
		"inventory_reduced=:inventory_reduced, locked=:locked, "
		"eq_name=:eq_name, eq_boil_size=:eq_boil_size, eq_efficiency=:eq_efficiency, "
                "eq_batch_size=:eq_batch_size, eq_tun_volume=:eq_tun_volume, eq_tun_weight=:eq_tun_weight, "
                "eq_tun_specific_heat=:eq_tun_specific_heat, eq_tun_material=:eq_tun_material, eq_tun_height=:eq_tun_height, "
                "eq_top_up_water=:eq_top_up_water, eq_trub_chiller_loss=:eq_chiller_loss, eq_evap_rate=:eq_evap_rate, "
                "eq_boil_time=:eq_boil_time, eq_calc_boil_volume=:eq_calcboil, eq_top_up_kettle=:eq_top_up_kettle, "
                "eq_hop_utilization=:eq_hopfactor, eq_notes=:eq_notes, eq_lauter_volume=:eq_lauter_volume, "
                "eq_lauter_height=:eq_lauter_height, eq_lauter_deadspace=:eq_lauter_deadspace, eq_kettle_volume=:eq_kettle_volume, "
                "eq_kettle_height=:eq_kettle_height, eq_mash_volume=:eq_mash_volume, eq_mash_max=:eq_mash_max, "
		"brew_date_start=:brew_date_start, brew_mash_ph=:brew_mash_ph, brew_mash_sg=:brew_mash_sg, "
		"brew_mash_efficiency=:brew_mash_efficiency, brew_sparge_temperature=:brew_sparge_temperature, "
		"brew_sparge_volume=:brew_sparge_volume, brew_sparge_est=:brew_sparge_est, brew_sparge_ph=:brew_sparge_ph, "
		"brew_preboil_volume=:brew_preboil_volume, brew_preboil_sg=:brew_preboil_sg, brew_preboil_ph=:brew_preboil_ph, "
		"brew_preboil_efficiency=:brew_preboil_efficiency, brew_aboil_volume=:brew_aboil_volume, "
		"brew_aboil_sg=:brew_aboil_sg, brew_aboil_ph=:brew_aboil_ph, brew_aboil_efficiency=:brew_aboil_efficiency, "
		"brew_cooling_method=:brew_cooling_method, brew_cooling_time=:brew_cooling_time, brew_cooling_to=:brew_cooling_to, "
		"brew_whirlpool9=:brew_whirlpool9, brew_whirlpool7=:brew_whirlpool7, brew_whirlpool6=:brew_whirlpool6, "
		"brew_whirlpool2=:brew_whirlpool2, brew_fermenter_volume=:brew_fermenter_volume, "
		"brew_fermenter_extrawater=:brew_fermenter_extrawater, brew_fermenter_tcloss=:brew_fermenter_tcloss, "
		"brew_aeration_time=:brew_aeration_time, brew_aeration_speed=:brew_aeration_speed, "
		"brew_aeration_type=:brew_aeration_type, brew_fermenter_sg=:brew_fermenter_sg, brew_fermenter_ibu=:brew_fermenter_ibu, "
		"brew_fermenter_color=:brew_fermenter_color, brew_date_end=:brew_date_end, "
		"og=:og, fg=:fg, primary_start_temp=:primary_start_temp, primary_max_temp=:primary_max_temp, "
		"primary_end_temp=:primary_end_temp, primary_end_sg=:primary_end_sg, primary_end_date=:primary_end_date, "
		"secondary_temp=:secondary_temp, secondary_end_sg=:secondary_end_sg, secondary_end_date=:secondary_end_date, "
		"tertiary_temp=:tertiary_temp, package_date=:package_date, package_volume=:package_volume, "
		"package_infuse_amount=:package_infuse_amount, package_infuse_abv=:package_infuse_abv, "
		"package_infuse_notes=:package_infuse_notes, package_abv=:package_abv, package_ph=:package_ph, "
		"bottle_amount=:bottle_amount, bottle_carbonation=:bottle_carbonation, bottle_priming_sugar=:bottle_priming_sugar, "
		"bottle_priming_amount=:bottle_priming_amount, bottle_priming_water=:bottle_priming_water, "
		"bottle_carbonation_temp=:bottle_carbonation_temp, keg_amount=:keg_amount, keg_carbonation=:keg_carbonation, "
		"keg_priming_sugar=:keg_priming_sugar, keg_priming_amount=:keg_priming_amount, keg_priming_water=:keg_priming_water, "
		"keg_carbonation_temp=:keg_carbonation_temp, keg_forced_carb=:keg_forced_carb, keg_pressure=:keg_pressure, "
		"taste_notes=:taste_notes, taste_rate=:taste_rate, taste_date=:taste_date, taste_color=:taste_color, "
		"taste_transparency=:taste_transparency, taste_head=:taste_head, taste_aroma=:taste_aroma, "
		"taste_taste=:taste_taste, taste_mouthfeel=:taste_mouthfeel, taste_aftertaste=:taste_aftertaste, "
		"st_name=:st_name, st_letter=:st_letter, "
		"st_guide=:st_guide, st_category=:st_category, st_category_number=:st_catnr, st_type=:st_type, "
		"st_og_min=:st_og_min, st_og_max=:st_og_max, st_fg_min=:st_fg_min, st_fg_max=:st_fg_max, "
		"st_ibu_min=:st_ibu_min, st_ibu_max=:st_ibu_max, st_color_min=:st_color_min, st_color_max=:st_color_max, "
		"st_carb_min=:st_carb_min, st_carb_max=:st_carb_max, st_abv_min=:st_abv_min, st_abv_max=:st_abv_max, "
		"type=:type, batch_size=:batch_size, boil_size=:boil_size, "
		"boil_time=:boil_time, efficiency=:efficiency, est_og=:est_og, est_og3=:est_og3, est_fg=:est_fg, est_abv=:est_abv, "
		"est_color=:est_color, color_method=:color_method, est_ibu=:est_ibu, ibu_method=:ibu_method, "
		"est_carb=:est_carb, sparge_temp=:sparge_temp, sparge_ph=:sparge_ph, "
		"sparge_volume=:sparge_volume, sparge_source=:sparge_source, sparge_acid_type=:sparge_acid_type, "
		"sparge_acid_perc=:sparge_acid_perc, sparge_acid_amount=:sparge_acid_amount, mash_ph=:mash_ph, "
		"mash_name=:mash_name, calc_acid=:calc_acid, "
		"w1_name=:w1_name, w1_amount=:w1_amount, w1_calcium=:w1_calcium, w1_sulfate=:w1_sulfate, "
		"w1_chloride=:w1_chloride, w1_sodium=:w1_sodium, w1_magnesium=:w1_magnesium, "
		"w1_total_alkalinity=:w1_total_alkalinity, w1_ph=:w1_ph, w1_cost=:w1_cost, "
		"w2_name=:w2_name, w2_amount=:w2_amount, w2_calcium=:w2_calcium, w2_sulfate=:w2_sulfate, "
                "w2_chloride=:w2_chloride, w2_sodium=:w2_sodium, w2_magnesium=:w2_magnesium, "
                "w2_total_alkalinity=:w2_total_alkalinity, w2_ph=:w2_ph, w2_cost=:w2_cost, "
		"wg_amount=:wg_amount, wg_calcium=:wg_calcium, wg_sulfate=:wg_sulfate, "
                "wg_chloride=:wg_chloride, wg_sodium=:wg_sodium, wg_magnesium=:wg_magnesium, "
                "wg_total_alkalinity=:wg_total_alkalinity, wg_ph=:wg_ph, "
		"wb_calcium=:wb_calcium, wb_sulfate=:wb_sulfate, wb_chloride=:wb_chloride, wb_sodium=:wb_sodium, "
		"wb_magnesium=:wb_magnesium, wb_total_alkalinity=:wb_total_alkalinity, wb_ph=:wb_ph, "
		"wa_acid_name=:wa_acid_name, wa_acid_perc=:wa_acid_perc, wa_base_name=:wa_base_name, "
		"starter_enable=:starter_enable, starter_type=:starter_type, starter_sg=:starter_sg, "
		"starter_viability=:starter_viability, yeast_prod_date=:yeast_prod_date, yeast_pitchrate=:yeast_pitchrate, "
		"prop1_type=:prop1_type, prop1_volume=:prop1_volume, prop2_type=:prop2_type, prop2_volume=:prop2_volume, "
		"prop3_type=:prop3_type, prop3_volume=:prop3_volume, prop4_type=:prop4_type, prop4_volume=:prop4_volume, "
		"divide_type=:divide_type, divide_size=:divide_size, divide_factor=:divide_factor, "
		"divide_parts=:divide_parts, divide_part=:divide_part, "
		"json_fermentables=:json_fermentables, json_hops=:json_hops, json_miscs=:json_miscs, "
		"json_yeasts=:json_yeasts, json_mashs=:json_mashs, uuid=:uuid");
    	} else {
	    query.prepare("UPDATE products SET "
		"name=:name, code=:code, birth=:birth, stage=:stage, notes=:notes, log_brew=:log_brew, "
                "log_fermentation=:log_fermentation, log_ispindel=:log_ispindel, log_co2pressure=:log_co2pressure, "
                "inventory_reduced=:inventory_reduced, locked=:locked, "
		"eq_name=:eq_name, eq_boil_size=:eq_boil_size, eq_efficiency=:eq_efficiency, "
                "eq_batch_size=:eq_batch_size, eq_tun_volume=:eq_tun_volume, eq_tun_weight=:eq_tun_weight, "
                "eq_tun_specific_heat=:eq_tun_specific_heat, eq_tun_material=:eq_tun_material, eq_tun_height=:eq_tun_height, "
                "eq_top_up_water=:eq_top_up_water, eq_trub_chiller_loss=:eq_chiller_loss, eq_evap_rate=:eq_evap_rate, "
                "eq_boil_time=:eq_boil_time, eq_calc_boil_volume=:eq_calcboil, eq_top_up_kettle=:eq_top_up_kettle, "
                "eq_hop_utilization=:eq_hopfactor, eq_notes=:eq_notes, eq_lauter_volume=:eq_lauter_volume, "
                "eq_lauter_height=:eq_lauter_height, eq_lauter_deadspace=:eq_lauter_deadspace, eq_kettle_volume=:eq_kettle_volume, "
                "eq_kettle_height=:eq_kettle_height, eq_mash_volume=:eq_mash_volume, eq_mash_max=:eq_mash_max, "
		"brew_date_start=:brew_date_start, brew_mash_ph=:brew_mash_ph, brew_mash_sg=:brew_mash_sg, "
                "brew_mash_efficiency=:brew_mash_efficiency, brew_sparge_temperature=:brew_sparge_temperature, "
                "brew_sparge_volume=:brew_sparge_volume, brew_sparge_est=:brew_sparge_est, brew_sparge_ph=:brew_sparge_ph, "
                "brew_preboil_volume=:brew_preboil_volume, brew_preboil_sg=:brew_preboil_sg, brew_preboil_ph=:brew_preboil_ph, "
                "brew_preboil_efficiency=:brew_preboil_efficiency, brew_aboil_volume=:brew_aboil_volume, "
                "brew_aboil_sg=:brew_aboil_sg, brew_aboil_ph=:brew_aboil_ph, brew_aboil_efficiency=:brew_aboil_efficiency, "
                "brew_cooling_method=:brew_cooling_method, brew_cooling_time=:brew_cooling_time, brew_cooling_to=:brew_cooling_to, "
                "brew_whirlpool9=:brew_whirlpool9, brew_whirlpool7=:brew_whirlpool7, brew_whirlpool6=:brew_whirlpool6, "
                "brew_whirlpool2=:brew_whirlpool2, brew_fermenter_volume=:brew_fermenter_volume, "
                "brew_fermenter_extrawater=:brew_fermenter_extrawater, brew_fermenter_tcloss=:brew_fermenter_tcloss, "
                "brew_aeration_time=:brew_aeration_time, brew_aeration_speed=:brew_aeration_speed, "
                "brew_aeration_type=:brew_aeration_type, brew_fermenter_sg=:brew_fermenter_sg, brew_fermenter_ibu=:brew_fermenter_ibu, "
                "brew_fermenter_color=:brew_fermenter_color, brew_date_end=:brew_date_end, "
                "og=:og, fg=:fg, primary_start_temp=:primary_start_temp, primary_max_temp=:primary_max_temp, "
                "primary_end_temp=:primary_end_temp, primary_end_sg=:primary_end_sg, primary_end_date=:primary_end_date, "
                "secondary_temp=:secondary_temp, secondary_end_sg=:secondary_end_sg, secondary_end_date=:secondary_end_date, "
                "tertiary_temp=:tertiary_temp, package_date=:package_date, package_volume=:package_volume, "
                "package_infuse_amount=:package_infuse_amount, package_infuse_abv=:package_infuse_abv, "
                "package_infuse_notes=:package_infuse_notes, package_abv=:package_abv, package_ph=:package_ph, "
                "bottle_amount=:bottle_amount, bottle_carbonation=:bottle_carbonation, bottle_priming_sugar=:bottle_priming_sugar, "
                "bottle_priming_amount=:bottle_priming_amount, bottle_priming_water=:bottle_priming_water, "
                "bottle_carbonation_temp=:bottle_carbonation_temp, keg_amount=:keg_amount, keg_carbonation=:keg_carbonation, "
                "keg_priming_sugar=:keg_priming_sugar, keg_priming_amount=:keg_priming_amount, keg_priming_water=:keg_priming_water, "
                "keg_carbonation_temp=:keg_carbonation_temp, keg_forced_carb=:keg_forced_carb, keg_pressure=:keg_pressure, "
                "taste_notes=:taste_notes, taste_rate=:taste_rate, taste_date=:taste_date, taste_color=:taste_color, "
                "taste_transparency=:taste_transparency, taste_head=:taste_head, taste_aroma=:taste_aroma, "
                "taste_taste=:taste_taste, taste_mouthfeel=:taste_mouthfeel, taste_aftertaste=:taste_aftertaste, "
		"st_name=:st_name, st_letter=:st_letter, "
		"st_guide=:st_guide, st_category=:st_category, st_category_number=:st_catnr, st_type=:st_type, "
                "st_og_min=:st_og_min, st_og_max=:st_og_max, st_fg_min=:st_fg_min, st_fg_max=:st_fg_max, "
                "st_ibu_min=:st_ibu_min, st_ibu_max=:st_ibu_max, st_color_min=:st_color_min, st_color_max=:st_color_max, "
                "st_carb_min=:st_carb_min, st_carb_max=:st_carb_max, st_abv_min=:st_abv_min, st_abv_max=:st_abv_max, "
                "type=:type, batch_size=:batch_size, boil_size=:boil_size, "
		"boil_time=:boil_time, efficiency=:efficiency, est_og=:est_og, est_og3=:est_og3, est_fg=:est_fg, est_abv=:est_abv, "
                "est_color=:est_color, color_method=:color_method, est_ibu=:est_ibu, ibu_method=:ibu_method, "
                "est_carb=:est_carb, sparge_temp=:sparge_temp, sparge_ph=:sparge_ph, "
		"sparge_volume=:sparge_volume, sparge_source=:sparge_source, sparge_acid_type=:sparge_acid_type, "
                "sparge_acid_perc=:sparge_acid_perc, sparge_acid_amount=:sparge_acid_amount, mash_ph=:mash_ph, "
                "mash_name=:mash_name, calc_acid=:calc_acid, "
                "w1_name=:w1_name, w1_amount=:w1_amount, w1_calcium=:w1_calcium, w1_sulfate=:w1_sulfate, "
                "w1_chloride=:w1_chloride, w1_sodium=:w1_sodium, w1_magnesium=:w1_magnesium, "
                "w1_total_alkalinity=:w1_total_alkalinity, w1_ph=:w1_ph, w1_cost=:w1_cost, "
                "w2_name=:w2_name, w2_amount=:w2_amount, w2_calcium=:w2_calcium, w2_sulfate=:w2_sulfate, "
                "w2_chloride=:w2_chloride, w2_sodium=:w2_sodium, w2_magnesium=:w2_magnesium, "
                "w2_total_alkalinity=:w2_total_alkalinity, w2_ph=:w2_ph, w2_cost=:w2_cost, "
                "wg_amount=:wg_amount, wg_calcium=:wg_calcium, wg_sulfate=:wg_sulfate, "
                "wg_chloride=:wg_chloride, wg_sodium=:wg_sodium, wg_magnesium=:wg_magnesium, "
                "wg_total_alkalinity=:wg_total_alkalinity, wg_ph=:wg_ph, "
                "wb_calcium=:wb_calcium, wb_sulfate=:wb_sulfate, wb_chloride=:wb_chloride, wb_sodium=:wb_sodium, "
                "wb_magnesium=:wb_magnesium, wb_total_alkalinity=:wb_total_alkalinity, wb_ph=:wb_ph, "
                "wa_acid_name=:wa_acid_name, wa_acid_perc=:wa_acid_perc, wa_base_name=:wa_base_name, "
		"starter_enable=:starter_enable, starter_type=:starter_type, starter_sg=:starter_sg, "
                "starter_viability=:starter_viability, yeast_prod_date=:yeast_prod_date, yeast_pitchrate=:yeast_pitchrate, "
                "prop1_type=:prop1_type, prop1_volume=:prop1_volume, prop2_type=:prop2_type, prop2_volume=:prop2_volume, "
                "prop3_type=:prop3_type, prop3_volume=:prop3_volume, prop4_type=:prop4_type, prop4_volume=:prop4_volume, "
                "divide_type=:divide_type, divide_size=:divide_size, divide_factor=:divide_factor, "
                "divide_parts=:divide_parts, divide_part=:divide_part, "
		"json_fermentables=:json_fermentables, json_hops=:json_hops, json_miscs=:json_miscs, "
                "json_yeasts=:json_yeasts, json_mashs=:json_mashs WHERE record = :recno");
    	}

	query.bindValue(":name", product->name);
	query.bindValue(":code", product->code);
	query.bindValue(":birth", product->birth);
	query.bindValue(":stage", product->stage);
        query.bindValue(":notes", product->notes);
	query.bindValue(":log_brew", product->log_brew ? 1:0);
	query.bindValue(":log_fermentation", product->log_fermentation ? 1:0);
	query.bindValue(":log_ispindel", product->log_ispindel ? 1:0);
	query.bindValue(":log_co2pressure", product->log_co2pressure ? 1:0);
	query.bindValue(":inventory_reduced", product->inventory_reduced);
	query.bindValue(":locked", product->locked ? 1:0);
	query.bindValue(":eq_name", product->eq_name);
        query.bindValue(":eq_boil_size", round(product->eq_boil_size * 10) / 10);
        query.bindValue(":eq_batch_size", round(product->eq_batch_size * 100) / 100);
        query.bindValue(":eq_tun_volume", round(product->eq_tun_volume * 10) / 10);
        query.bindValue(":eq_tun_weight", round(product->eq_tun_weight * 10) / 10);
        query.bindValue(":eq_tun_specific_heat", round(product->eq_tun_specific_heat * 1000) / 1000);
        query.bindValue(":eq_tun_material", product->eq_tun_material);
        query.bindValue(":eq_tun_height", round(product->eq_tun_height * 1000) / 1000);
        query.bindValue(":eq_top_up_water", round(product->eq_top_up_water * 10) / 10);
        query.bindValue(":eq_chiller_loss", round(product->eq_trub_chiller_loss * 10) / 10);
        query.bindValue(":eq_evap_rate", round(product->eq_evap_rate * 100) / 100);
        query.bindValue(":eq_boil_time", round(product->eq_boil_time));
        query.bindValue(":eq_calcboil", product->eq_calc_boil_volume ? 1:0);
        query.bindValue(":eq_top_up_kettle", round(product->eq_top_up_kettle * 10) / 10);
        query.bindValue(":eq_hopfactor", round(product->eq_hop_utilization));
        query.bindValue(":eq_notes", product->eq_notes);
        query.bindValue(":eq_lauter_volume", round(product->eq_lauter_volume * 10) / 10);
        query.bindValue(":eq_lauter_height", round(product->eq_lauter_height * 1000) / 1000);
        query.bindValue(":eq_lauter_deadspace", round(product->eq_lauter_deadspace * 10) / 10);
        query.bindValue(":eq_kettle_volume", round(product->eq_kettle_volume * 10) / 10);
        query.bindValue(":eq_kettle_height", round(product->eq_kettle_height * 1000) / 1000);
        query.bindValue(":eq_mash_volume", round(product->eq_mash_volume * 10) / 10);
        query.bindValue(":eq_mash_max", round(product->eq_mash_max * 10) / 10);
        query.bindValue(":eq_efficiency", round(product->eq_efficiency * 10) / 10);
	query.bindValue(":brew_date_start", product->brew_date_start);
	query.bindValue(":brew_mash_ph", round(product->brew_mash_ph * 100) / 100);
	query.bindValue(":brew_mash_sg", round(product->brew_mash_sg * 1000) / 1000);
	query.bindValue(":brew_mash_efficiency", round(product->brew_mash_efficiency * 10) / 10);
	query.bindValue(":brew_sparge_temperature", round(product->brew_sparge_temperature * 10) / 10);
	query.bindValue(":brew_sparge_volume", round(product->brew_sparge_volume * 10) / 10);
	query.bindValue(":brew_sparge_est", round(product->brew_sparge_est * 10) / 10);
	query.bindValue(":brew_sparge_ph", round(product->brew_sparge_ph * 100) / 100);
	query.bindValue(":brew_preboil_volume", round(product->brew_preboil_volume * 10) / 10);
	query.bindValue(":brew_preboil_sg", round(product->brew_preboil_sg * 1000) / 1000);
	query.bindValue(":brew_preboil_ph", round(product->brew_preboil_ph * 100) / 100);
	query.bindValue(":brew_preboil_efficiency", round(product->brew_preboil_efficiency * 10) / 10);
	query.bindValue(":brew_aboil_volume", round(product->brew_aboil_volume * 10) / 10);
	query.bindValue(":brew_aboil_sg", round(product->brew_aboil_sg * 1000) / 1000);
	query.bindValue(":brew_aboil_ph", round(product->brew_aboil_ph * 100) / 100);
	query.bindValue(":brew_aboil_efficiency", round(product->brew_aboil_efficiency * 10) / 10);
	query.bindValue(":brew_cooling_method", product->brew_cooling_method);
	query.bindValue(":brew_cooling_time", round(product->brew_cooling_time * 10) / 10);
	query.bindValue(":brew_cooling_to", round(product->brew_cooling_to * 10) / 10);
	query.bindValue(":brew_whirlpool9", round(product->brew_whirlpool9));
	query.bindValue(":brew_whirlpool7", round(product->brew_whirlpool7));
	query.bindValue(":brew_whirlpool6", round(product->brew_whirlpool6));
	query.bindValue(":brew_whirlpool2", round(product->brew_whirlpool2));
	query.bindValue(":brew_fermenter_volume", round(product->brew_fermenter_volume * 10) / 10);
	query.bindValue(":brew_fermenter_extrawater", round(product->brew_fermenter_extrawater * 10) / 10);
	query.bindValue(":brew_fermenter_tcloss", round(product->brew_fermenter_tcloss * 10) / 10);
	query.bindValue(":brew_aeration_time", round(product->brew_aeration_time));
	query.bindValue(":brew_aeration_speed", round(product->brew_aeration_speed * 10) / 10);
	query.bindValue(":brew_aeration_type", product->brew_aeration_type);
	query.bindValue(":brew_fermenter_sg", round(product->brew_fermenter_sg * 1000) / 1000);
	query.bindValue(":brew_fermenter_ibu", round(product->brew_fermenter_ibu * 10) / 10);
	query.bindValue(":brew_fermenter_color", round(product->brew_fermenter_color * 10) / 10);
	query.bindValue(":brew_date_end", product->brew_date_end);
	query.bindValue(":og", round(product->og * 1000) / 1000);
	query.bindValue(":fg", round(product->fg * 1000) / 1000);
	query.bindValue(":primary_start_temp", round(product->primary_start_temp * 10) / 10);
	query.bindValue(":primary_max_temp", round(product->primary_max_temp * 10) / 10);
	query.bindValue(":primary_end_temp", round(product->primary_end_temp * 10) / 10);
	query.bindValue(":primary_end_sg", round(product->primary_end_sg * 1000) / 1000);
	query.bindValue(":primary_end_date", product->primary_end_date);
	query.bindValue(":secondary_temp", round(product->secondary_temp * 10) / 10);
	query.bindValue(":secondary_end_sg", round(product->secondary_end_sg * 1000) / 1000);
	query.bindValue(":secondary_end_date", product->secondary_end_date);
	query.bindValue(":tertiary_temp", round(product->tertiary_temp * 10) / 10);
	query.bindValue(":package_date", product->package_date);
	query.bindValue(":package_volume", round(product->package_volume * 10) / 10);
	query.bindValue(":package_infuse_amount", round(product->package_infuse_amount * 10) / 10);
	query.bindValue(":package_infuse_abv", round(product->package_infuse_abv * 100) / 100);
	query.bindValue(":package_infuse_notes", product->package_infuse_notes);
	query.bindValue(":package_abv", round(product->package_abv * 100) / 100);
	query.bindValue(":package_ph", round(product->package_ph * 100) / 100);
	query.bindValue(":bottle_amount", round(product->bottle_amount * 10) / 10);
	query.bindValue(":bottle_carbonation", round(product->bottle_carbonation * 100) / 100);
	query.bindValue(":bottle_priming_sugar", product->bottle_priming_sugar);
	query.bindValue(":bottle_priming_amount", round(product->bottle_priming_amount * 10) / 10);
	query.bindValue(":bottle_priming_water", round(product->bottle_priming_water * 1000) / 1000);
	query.bindValue(":bottle_carbonation_temp", round(product->bottle_carbonation_temp * 10) / 10);
	query.bindValue(":keg_amount", round(product->keg_amount * 10) / 10);
	query.bindValue(":keg_carbonation", round(product->keg_carbonation * 100) / 100);
	query.bindValue(":keg_priming_sugar", product->keg_priming_sugar);
	query.bindValue(":keg_priming_amount", round(product->keg_priming_amount * 10) / 10);
	query.bindValue(":keg_priming_water", round(product->keg_priming_water * 1000) / 1000);
	query.bindValue(":keg_carbonation_temp", round(product->keg_carbonation_temp * 10) / 10);
	query.bindValue(":keg_forced_carb", product->keg_forced_carb ? 1:0);
	query.bindValue(":keg_pressure", round(product->keg_pressure * 10) / 10);
	query.bindValue(":taste_notes", product->taste_notes);
	query.bindValue(":taste_rate", round(product->taste_rate * 10) / 10);
	query.bindValue(":taste_date", product->taste_date);
	query.bindValue(":taste_color", product->taste_color);
	query.bindValue(":taste_transparency", product->taste_transparency);
	query.bindValue(":taste_head", product->taste_head);
	query.bindValue(":taste_aroma", product->taste_aroma);
	query.bindValue(":taste_taste", product->taste_taste);
	query.bindValue(":taste_mouthfeel", product->taste_mouthfeel);
	query.bindValue(":taste_aftertaste", product->taste_aftertaste);
	query.bindValue(":st_name", product->st_name);
	query.bindValue(":st_letter", product->st_letter);
	query.bindValue(":st_guide", product->st_guide);
	query.bindValue(":st_category", product->st_category);
	query.bindValue(":st_catnr", product->st_category_number);
	query.bindValue(":st_type", product->st_type);
	query.bindValue(":st_og_min", round(product->st_og_min * 1000) / 1000);
	query.bindValue(":st_og_max", round(product->st_og_max * 1000) / 1000);
	query.bindValue(":st_fg_min", round(product->st_fg_min * 1000) / 1000);
        query.bindValue(":st_fg_max", round(product->st_fg_max * 1000) / 1000);
	query.bindValue(":st_ibu_min", round(product->st_ibu_min * 10) / 10);
        query.bindValue(":st_ibu_max", round(product->st_ibu_max * 10) / 10);
	query.bindValue(":st_color_min", round(product->st_color_min * 10) / 10);
        query.bindValue(":st_color_max", round(product->st_color_max * 10) / 10);
	query.bindValue(":st_carb_min", round(product->st_carb_min * 10) / 10);
        query.bindValue(":st_carb_max", round(product->st_carb_max * 10) / 10);
	query.bindValue(":st_abv_min", round(product->st_abv_min * 10) / 10);
        query.bindValue(":st_abv_max", round(product->st_abv_max * 10) / 10);
	query.bindValue(":type", product->type);
	query.bindValue(":batch_size", round(product->batch_size * 10) / 10);
	query.bindValue(":boil_size", round(product->boil_size * 10) / 10);
	query.bindValue(":boil_time", round(product->boil_time * 10) / 10);
	query.bindValue(":efficiency", round(product->efficiency * 10) / 10);
	query.bindValue(":est_og", round(product->est_og * 1000) / 1000);
	query.bindValue(":est_og3", round(product->est_og3 * 1000) / 1000);
	query.bindValue(":est_fg", round(product->est_fg * 1000) / 1000);
	query.bindValue(":est_abv", round(product->est_abv * 10) / 10);
	query.bindValue(":est_color", round(product->est_color * 10) / 10);
	query.bindValue(":color_method", product->color_method);
	query.bindValue(":est_ibu", round(product->est_ibu * 10) / 10);
	query.bindValue(":ibu_method", product->ibu_method);
	query.bindValue(":est_carb", round(product->est_carb * 10) / 10);
	query.bindValue(":sparge_temp", round(product->sparge_temp * 10) / 10);
	query.bindValue(":sparge_ph", round(product->sparge_ph * 100) / 100);
	query.bindValue(":sparge_volume", round(product->sparge_volume * 10) / 10);
	query.bindValue(":sparge_source", product->sparge_source);
	query.bindValue(":sparge_acid_type", product->sparge_acid_type);
	query.bindValue(":sparge_acid_perc", round(product->sparge_acid_perc * 10) / 10);
	query.bindValue(":sparge_acid_amount", round(product->sparge_acid_amount * 100000) / 100000);
	query.bindValue(":mash_ph", round(product->mash_ph * 100) / 100);
	query.bindValue(":mash_name", product->mash_name);
	query.bindValue(":calc_acid", product->calc_acid ?1:0);
	query.bindValue(":w1_name", product->w1_name);
	query.bindValue(":w1_amount", round(product->w1_amount * 10) / 10);
	query.bindValue(":w1_calcium", round(product->w1_calcium * 100000) / 100000);
	query.bindValue(":w1_sulfate", round(product->w1_sulfate * 100000) / 100000);
	query.bindValue(":w1_chloride", round(product->w1_chloride * 100000) / 100000);
	query.bindValue(":w1_sodium", round(product->w1_sodium * 100000) / 100000);
	query.bindValue(":w1_magnesium", round(product->w1_magnesium * 100000) / 100000);
	query.bindValue(":w1_total_alkalinity", round(product->w1_total_alkalinity * 100000) / 100000);
	query.bindValue(":w1_ph", round(product->w1_ph * 100) / 100);
	query.bindValue(":w1_cost", round(product->w1_cost * 100) / 100);
	query.bindValue(":w2_name", product->w2_name);
        query.bindValue(":w2_amount", round(product->w2_amount * 10) / 10);
        query.bindValue(":w2_calcium", round(product->w2_calcium * 100000) / 100000);
        query.bindValue(":w2_sulfate", round(product->w2_sulfate * 100000) / 100000);
        query.bindValue(":w2_chloride", round(product->w2_chloride * 100000) / 100000);
        query.bindValue(":w2_sodium", round(product->w2_sodium * 100000) / 100000);
        query.bindValue(":w2_magnesium", round(product->w2_magnesium * 100000) / 100000);
        query.bindValue(":w2_total_alkalinity", round(product->w2_total_alkalinity * 100000) / 100000);
        query.bindValue(":w2_ph", round(product->w2_ph * 100) / 100);
        query.bindValue(":w2_cost", round(product->w2_cost * 100) / 100);
	query.bindValue(":wg_amount", round(product->wg_amount * 10) / 10);
        query.bindValue(":wg_calcium", round(product->wg_calcium * 100000) / 100000);
        query.bindValue(":wg_sulfate", round(product->wg_sulfate * 100000) / 100000);
        query.bindValue(":wg_chloride", round(product->wg_chloride * 100000) / 100000);
        query.bindValue(":wg_sodium", round(product->wg_sodium * 100000) / 100000);
        query.bindValue(":wg_magnesium", round(product->wg_magnesium * 100000) / 100000);
        query.bindValue(":wg_total_alkalinity", round(product->wg_total_alkalinity * 100000) / 100000);
        query.bindValue(":wg_ph", round(product->wg_ph * 100) / 100);
	query.bindValue(":wb_calcium", round(product->wb_calcium * 100000) / 100000);
        query.bindValue(":wb_sulfate", round(product->wb_sulfate * 100000) / 100000);
        query.bindValue(":wb_chloride", round(product->wb_chloride * 100000) / 100000);
        query.bindValue(":wb_sodium", round(product->wb_sodium * 100000) / 100000);
        query.bindValue(":wb_magnesium", round(product->wb_magnesium * 100000) / 100000);
        query.bindValue(":wb_total_alkalinity", round(product->wb_total_alkalinity * 100000) / 100000);
        query.bindValue(":wb_ph", round(product->wb_ph * 100) / 100);
	query.bindValue(":wa_acid_name", product->wa_acid_name);
	query.bindValue(":wa_acid_perc", round(product->wa_acid_perc * 10) / 10);
	query.bindValue(":wa_base_name", product->wa_base_name);
	query.bindValue(":starter_enable", product->starter_enable ? 1:0);
	query.bindValue(":starter_type", product->starter_type);
	query.bindValue(":starter_sg", round(product->starter_sg * 1000) / 1000);
	query.bindValue(":starter_viability", product->starter_viability);
	query.bindValue(":yeast_prod_date", product->yeast_prod_date);
	query.bindValue(":yeast_pitchrate", round(product->yeast_pitchrate * 1000) / 1000);
	query.bindValue(":prop1_type", product->prop_type[0]);
	query.bindValue(":prop1_volume", round(product->prop_volume[0] * 1000) / 1000);
	query.bindValue(":prop2_type", product->prop_type[1]);
	query.bindValue(":prop2_volume", round(product->prop_volume[1] * 1000) / 1000);
	query.bindValue(":prop3_type", product->prop_type[2]);
	query.bindValue(":prop3_volume", round(product->prop_volume[2] * 1000) / 1000);
	query.bindValue(":prop4_type", product->prop_type[3]);
	query.bindValue(":prop4_volume", round(product->prop_volume[3] * 1000) / 1000);
	query.bindValue(":divide_type", product->divide_type);
	query.bindValue(":divide_size", round(product->divide_size * 10) / 10);
	query.bindValue(":divide_factor", round(product->divide_factor * 100) / 100);
	query.bindValue(":divide_parts", product->divide_parts);
	query.bindValue(":divide_part", product->divide_part);

	if (product->fermentables.size() == 0) {
	    query.bindValue(":json_fermentables", "[]");
	} else {
	    QJsonArray array;
	    for (int i = 0; i < product->fermentables.size(); i++) {
		QJsonObject obj;
		obj.insert("f_name", product->fermentables.at(i).f_name);
		obj.insert("f_origin", product->fermentables.at(i).f_origin);
		obj.insert("f_supplier", product->fermentables.at(i).f_supplier);
		obj.insert("f_amount", round(product->fermentables.at(i).f_amount * 10000) / 10000);
		obj.insert("f_cost", round(product->fermentables.at(i).f_cost * 1000) / 1000);
		obj.insert("f_type", product->fermentables.at(i).f_type);
		obj.insert("f_yield", round(product->fermentables.at(i).f_yield * 10) / 10);
		obj.insert("f_color", round(product->fermentables.at(i).f_color * 10) / 10);
		obj.insert("f_coarse_fine_diff", round(product->fermentables.at(i).f_coarse_fine_diff * 10) / 10);
		obj.insert("f_moisture", round(product->fermentables.at(i).f_moisture * 10) / 10);
		obj.insert("f_diastatic_power", round(product->fermentables.at(i).f_diastatic_power * 100000) / 100000);
		obj.insert("f_protein", round(product->fermentables.at(i).f_protein * 10) / 10);
		obj.insert("f_dissolved_protein", round(product->fermentables.at(i).f_dissolved_protein * 10) / 10);
		obj.insert("f_max_in_batch", product->fermentables.at(i).f_max_in_batch);
		obj.insert("f_graintype", product->fermentables.at(i).f_graintype);
		obj.insert("f_added", product->fermentables.at(i).f_added);
		obj.insert("f_recommend_mash", product->fermentables.at(i).f_recommend_mash ? 1:0);
		obj.insert("f_add_after_boil", product->fermentables.at(i).f_add_after_boil ? 1:0);
		obj.insert("f_adjust_to_total_100", product->fermentables.at(i).f_adjust_to_total_100 ? 1:0);
		obj.insert("f_percentage", round(product->fermentables.at(i).f_percentage * 10) / 10);
		obj.insert("f_di_ph", round(product->fermentables.at(i).f_di_ph * 100000) / 100000);
		obj.insert("f_acid_to_ph_57", round(product->fermentables.at(i).f_acid_to_ph_57 * 100000) / 100000);
		qDebug() << "fermentables_Json" << i << obj;
		array.append(obj);      /* Append this object */
	    }
	    QJsonDocument doc;
	    doc.setArray(array);
//	    qDebug() << doc.toJson(QJsonDocument::Compact);
	    query.bindValue(":json_fermentables", doc.toJson(QJsonDocument::Compact));
	}

	if (product->hops.size() == 0) {
            query.bindValue(":json_hops", "[]");
        } else {
            QJsonArray array;
            for (int i = 0; i < product->hops.size(); i++) {
                QJsonObject obj;
		obj.insert("h_name", product->hops.at(i).h_name);
                obj.insert("h_origin", product->hops.at(i).h_origin);
		obj.insert("h_amount", round(product->hops.at(i).h_amount * 10000) / 10000);
		obj.insert("h_cost", round(product->hops.at(i).h_cost * 100) / 100);
		obj.insert("h_type", product->hops.at(i).h_type);
		obj.insert("h_form", product->hops.at(i).h_form);
		obj.insert("h_useat", product->hops.at(i).h_useat);
		obj.insert("h_time", round(product->hops.at(i).h_time));
		obj.insert("h_alpha", round(product->hops.at(i).h_alpha * 100) / 100);
		obj.insert("h_beta", round(product->hops.at(i).h_beta * 100) / 100);
		obj.insert("h_hsi", round(product->hops.at(i).h_hsi * 100) / 100);
		obj.insert("h_humulene", round(product->hops.at(i).h_humulene * 100) / 100);
		obj.insert("h_caryophyllene", round(product->hops.at(i).h_caryophyllene * 100) / 100);
		obj.insert("h_cohumulone", round(product->hops.at(i).h_cohumulone * 100) / 100);
		obj.insert("h_myrcene", round(product->hops.at(i).h_myrcene * 100) / 100);
		obj.insert("h_total_oil", round(product->hops.at(i).h_total_oil * 100) / 100);
		qDebug() << "hops_Json" << i << obj;
                array.append(obj);      /* Append this object */
            }
	    QJsonDocument doc;
            doc.setArray(array);
            query.bindValue(":json_hops", doc.toJson(QJsonDocument::Compact));
        }

	if (product->miscs.size() == 0) {
            query.bindValue(":json_miscs", "[]");
        } else {
            QJsonArray array;
            for (int i = 0; i < product->miscs.size(); i++) {
                QJsonObject obj;
		obj.insert("m_name", product->miscs.at(i).m_name);
		obj.insert("m_amount", round(product->miscs.at(i).m_amount * 10000) / 10000);
		obj.insert("m_type", product->miscs.at(i).m_type);
		obj.insert("m_use_use", product->miscs.at(i).m_use_use);
		obj.insert("m_time", round(product->miscs.at(i).m_time));
		obj.insert("m_amount_is_weight", product->miscs.at(i).m_amount_is_weight ? 1:0);
		obj.insert("m_cost", round(product->miscs.at(i).m_cost * 10000) / 10000);
                qDebug() << "miscs_Json" << i << obj;
                array.append(obj);      /* Append this object */
            }
	    QJsonDocument doc;
            doc.setArray(array);
//            qDebug() << doc.toJson(QJsonDocument::Compact);
            query.bindValue(":json_miscs", doc.toJson(QJsonDocument::Compact));
        }

	if (product->yeasts.size() == 0) {
            query.bindValue(":json_yeasts", "[]");
        } else {
            QJsonArray array;
            for (int i = 0; i < product->yeasts.size(); i++) {
                QJsonObject obj;
		obj.insert("y_name", product->yeasts.at(i).y_name);
		obj.insert("y_laboratory", product->yeasts.at(i).y_laboratory);
		obj.insert("y_product_id", product->yeasts.at(i).y_product_id);
		obj.insert("y_amount", round(product->yeasts.at(i).y_amount * 10000) / 10000);
		obj.insert("y_type", product->yeasts.at(i).y_type);
		obj.insert("y_form", product->yeasts.at(i).y_form);
		obj.insert("y_min_temperature", round(product->yeasts.at(i).y_min_temperature * 10) / 10);
		obj.insert("y_max_temperature", round(product->yeasts.at(i).y_max_temperature * 10) / 10);
		obj.insert("y_flocculation", product->yeasts.at(i).y_flocculation);
		obj.insert("y_attenuation", round(product->yeasts.at(i).y_attenuation * 10) / 10);
		obj.insert("y_cells", product->yeasts.at(i).y_cells);
		obj.insert("y_tolerance", round(product->yeasts.at(i).y_tolerance * 10) / 10);
		obj.insert("y_inventory", round(product->yeasts.at(i).y_inventory * 10000) / 10000);
		obj.insert("y_use", product->yeasts.at(i).y_use);
		obj.insert("y_sta1", product->yeasts.at(i).y_sta1 ? 1:0);
		obj.insert("y_bacteria", product->yeasts.at(i).y_bacteria ? 1:0);
		obj.insert("y_harvest_top", product->yeasts.at(i).y_harvest_top ? 1:0);
		obj.insert("y_harvest_time", product->yeasts.at(i).y_harvest_time);
		obj.insert("y_pitch_temperature", round(product->yeasts.at(i).y_pitch_temperature * 10) / 10);
		obj.insert("y_pofpos", product->yeasts.at(i).y_pofpos ? 1:0);
		obj.insert("y_zymocide", product->yeasts.at(i).y_zymocide);
		obj.insert("y_gr_hl_lo", product->yeasts.at(i).y_gr_hl_lo);
		obj.insert("y_sg_lo", round(product->yeasts.at(i).y_sg_lo * 1000) / 1000);
		obj.insert("y_gr_hl_hi", product->yeasts.at(i).y_gr_hl_hi);
		obj.insert("y_sg_hi", round(product->yeasts.at(i).y_sg_hi * 1000) / 1000);
		obj.insert("y_cost", round(product->yeasts.at(i).y_cost * 1000) / 1000);
                qDebug() << "yeasts_Json" << i << obj;
                array.append(obj);      /* Append this object */
            }
	    QJsonDocument doc;
            doc.setArray(array);
//            qDebug() << doc.toJson(QJsonDocument::Compact);
            query.bindValue(":json_yeasts", doc.toJson(QJsonDocument::Compact));
        }

	if (product->mashs.size() == 0) {
            query.bindValue(":json_mashs", "[]");
	    qDebug() << "Saved empty mashs";
        } else {
            QJsonArray array;
            for (int i = 0; i < product->mashs.size(); i++) {
                QJsonObject obj;
		obj.insert("step_name", product->mashs.at(i).step_name);
		obj.insert("step_type", product->mashs.at(i).step_type);
		obj.insert("step_volume", round(product->mashs.at(i).step_volume * 100) / 100);
		obj.insert("step_infuse_amount", round(product->mashs.at(i).step_infuse_amount * 100) / 100);
		obj.insert("step_infuse_temp", round(product->mashs.at(i).step_infuse_temp * 100) / 100);
		obj.insert("step_temp", round(product->mashs.at(i).step_temp * 100) / 100);
		obj.insert("step_time", round(product->mashs.at(i).step_time * 100) / 100);
		obj.insert("ramp_time", round(product->mashs.at(i).ramp_time * 100) / 100);
		obj.insert("end_temp", round(product->mashs.at(i).end_temp * 100) / 100);
		obj.insert("step_wg_ratio", round(product->mashs.at(i).step_wg_ratio * 100) / 100);
		obj.insert("step_ph", round(product->mashs.at(i).step_ph * 100) / 100);
		obj.insert("step_sg", round(product->mashs.at(i).step_sg * 10000) / 10000);
                qDebug() << "mashs_Json" << i << obj;
                array.append(obj);      /* Append this object */
            }
	    QJsonDocument doc;
            doc.setArray(array);
            qDebug() << doc.toJson(QJsonDocument::Compact);
            query.bindValue(":json_mashs", doc.toJson(QJsonDocument::Compact));
        }

	if (this->recno == -1) {
	    query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36));
	} else {
	    query.bindValue(":recno", this->recno);
	}
	query.exec();
	qDebug() << query.lastQuery();
	if (query.lastError().isValid()) {
	    qDebug() << "EditProduct" << query.lastError();
	    QMessageBox::warning(this, tr("Database error"),
                        tr("MySQL error: %1\n%2\n%3")
                        .arg(query.lastError().nativeErrorCode())
                        .arg(query.lastError().driverText())
                        .arg(query.lastError().databaseText()));
	} else {
	    /*
	     * If this was a new product, find out what record number we
	     * have got and set it. So when the user saves this record
	     * again, it will be updated instead of inserting a new copy.
	     */
	    if (this->recno < 0) {
		QVariant id = query.lastInsertId();
		this->recno = product->record = id.toInt();
		qDebug() << "EditProduct Inserted record" << this->recno;
	    } else {
		qDebug() << "EditProduct Updated record" << this->recno;
	    }
	}
    }

    ui->saveButton->setEnabled(false);
    this->textIsChanged = false;
    WindowTitle();
}


#include "EditProductTab1.cpp"
#include "EditProductTab2.cpp"
#include "EditProductTab3.cpp"
#include "EditProductTab4.cpp"
#include "EditProductTab5.cpp"
#include "EditProductTab6.cpp"
#include "EditProductTab7.cpp"
#include "EditProductTab8.cpp"
#include "EditProductTab9.cpp"
#include "EditProductTab10.cpp"
#include "EditProductTab11.cpp"
#include "EditProductExport.cpp"


void EditProduct::on_deleteButton_clicked()
{
    QSqlQuery query;

    int rc = QMessageBox::warning(this, tr("Delete product"), tr("Delete %1").arg(product->name),
                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if (rc == QMessageBox::No)
        return;

    query.prepare("DELETE FROM products WHERE record = :recno");
    query.bindValue(":recno", this->recno);
    query.exec();
    if (query.lastError().isValid()) {
	qDebug() << "EditProduct" << query.lastError();
	QMessageBox::warning(this, tr("Database error"),
                        tr("MySQL error: %1\n%2\n%3")
                        .arg(query.lastError().nativeErrorCode())
                        .arg(query.lastError().driverText())
                        .arg(query.lastError().databaseText()));
    } else {
	qDebug() << "EditProduct Deleted" << this->recno;
    }

    this->close();
    this->setResult(1);
}


void EditProduct::is_changed()
{
    ui->saveButton->setEnabled(true);
    ui->deleteButton->setEnabled(((this->recno >= 0) ? true:false) && ! product->locked);
    this->textIsChanged = true;
    WindowTitle();
}


void EditProduct::on_quitButton_clicked()
{
    if (this->textIsChanged) {
	int rc = QMessageBox::warning(this, tr("Product changed"), tr("The product has been modified. Save changes?"),
                                QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save);
        switch (rc) {
            case QMessageBox::Save:
                        on_saveButton_clicked();
                        break;  /* Saved and then Quit */
            case QMessageBox::Discard:
                        break;  /* Quit without Save */
            case QMessageBox::Cancel:
                        return; /* Return to the editor page */
        }
    }

    this->close();
    this->setResult(1);
}

mercurial