src/EditRecipeExport.cpp

Fri, 29 Jul 2022 13:12:26 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 29 Jul 2022 13:12:26 +0200
changeset 373
b02aca4e926c
parent 340
b9af88bfe972
child 384
d68826df8b63
permissions
-rw-r--r--

First load of changes for hops. In EditHop load the dropdown buttons from the global table. Use named query fields. Added database utilisation and bu_factor fields for hop extracts. Added edit fields for these new fields. Added post boil SG, utilisation and bu_factor parameters to the toIBU function. Added hops form parameter to the hopFlavourContribution and hopAromaContribution display bars. In the hops inventory list dispay volumes instead of weight for hop extracts. Modified the TinsethIBU function to use utilisation and bu_factor parameters. Add calculations for co2 and iso hop extracts, this is work in progress. The toIBU function makes use of the preSG and postSG values to use the correct SG to caall the TinsethIBU function. This results in a bit lower IBU values mostly affecting the late additions. Added use hop at bottling for iso hop extracts like Tetra hops using the formula from BarthHaas.

/**
 * EditRecipe.cpp is part of bmsapp.
 *
 * Export recipe.
 *
 * 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/>.
 */


void EditRecipe::exportBeerXML()
{
    const QStringList color_method({ "Morey", "Mosher", "Daniels", "Halberstadt", "Naudts" });
    const QStringList hop_forms({ "Pellet", "Plug", "Leaf", "Leaf", "Pellet", "Pellet" });
    /*                                                    "Leaf Wet", "Cryo", "Extract" */
    /*  We use more hop forms then beerxml knows about, so we send known names */
    /*  instead of what we internally use. */

    /*  Note: Whirlpool hop is Aroma + time. Hop at flame-off is Aroma with time = 0. BeerXML is really stupid designed. */
    /*                                                flame-off vv        vv whirlpool hop */
    const QStringList hop_use({ "Mash", "First wort", "Boil", "Aroma", "Aroma", "Dry hop", "Dry hop" });
    const QStringList yeast_type({ "Lager", "Ale", "Wheat", "Wine", "Champagne", "Other", "Other", "Other", "Other", "Other" });
    const QStringList yeast_form({ "Liquid", "Dry", "Slant", "Culture", "Frozen", "Bottle", "Dry" });

    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::homePath() + "/" + recipe->name + ".xml", tr("Files (*.xml)"));
    if (fileName == 0) {
        QMessageBox::warning(this, tr("Save File"), tr("No XML file selected."));
        return;
    }

    qInfo() << "Recipe to beerXML" << fileName;
    QFile file(fileName);
    file.open(QIODevice::WriteOnly);

    QXmlStreamWriter *xmlWriter = new QXmlStreamWriter(&file);
    xmlWriter->writeStartDocument();
    xmlWriter->setAutoFormatting(true);
    xmlWriter->setAutoFormattingIndent(1);

    xmlWriter->writeStartElement("RECIPES");
    xmlWriter->writeStartElement("RECIPE");
    /*
     * Recipe basics
     */
    xmlWriter->writeTextElement("VERSION", "1");
    xmlWriter->writeTextElement("NAME", recipe->name);
    if (recipe->notes != "")
	xmlWriter->writeTextElement("NOTES", recipe->notes);
    xmlWriter->writeTextElement("TYPE", g_recipe_types[recipe->type]);
    xmlWriter->writeTextElement("BREWER", "Anonymous");
    xmlWriter->writeTextElement("BATCH_SIZE", QString::number(recipe->batch_size, 'f', 4));
    xmlWriter->writeTextElement("BOIL_SIZE", QString::number(recipe->boil_size, 'f', 4));
    xmlWriter->writeTextElement("BOIL_TIME", QString::number(recipe->boil_time, 'f', 3));
    xmlWriter->writeTextElement("EFFICIENCY", QString::number(recipe->efficiency, 'f', 4));
    xmlWriter->writeTextElement("EST_OG", QString::number(recipe->est_og, 'f', 3));
    xmlWriter->writeTextElement("EST_FG", QString::number(recipe->est_fg, 'f', 3));
    if (recipe->est_abv > 0)
    	xmlWriter->writeTextElement("EST_ABV", QString::number(recipe->est_abv, 'f', 1));
    if (recipe->est_color > 0) {
	xmlWriter->writeTextElement("EST_COLOR", QString::number(Utils::ebc_to_srm(recipe->est_color), 'f', 6));
	xmlWriter->writeTextElement("COLOR_METHOD", color_method[recipe->color_method]);
    }
    if (recipe->est_ibu > 0) {
	xmlWriter->writeTextElement("EST_IBU", QString::number(recipe->est_ibu, 'f', 1));
	xmlWriter->writeTextElement("IBU_METHOD", g_ibu_method[0]);	// Only Tinseth
    }

    xmlWriter->writeStartElement("STYLE");
	xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", recipe->st_name);
	xmlWriter->writeTextElement("CATEGORY", recipe->st_category);
	xmlWriter->writeTextElement("CATEGORY_NUMBER", QString::number(recipe->st_category_number, 'f', 0));
	xmlWriter->writeTextElement("STYLE_LETTER", recipe->st_letter);
	xmlWriter->writeTextElement("STYLE_GUIDE", recipe->st_guide);
	xmlWriter->writeTextElement("TYPE", g_style_types[recipe->st_type]);
	xmlWriter->writeTextElement("OG_MIN", QString::number(recipe->st_og_min, 'f', 3));
	xmlWriter->writeTextElement("OG_MAX", QString::number(recipe->st_og_max, 'f', 3));
	xmlWriter->writeTextElement("FG_MIN", QString::number(recipe->st_fg_min, 'f', 3));
	xmlWriter->writeTextElement("FG_MAX", QString::number(recipe->st_fg_max, 'f', 3));
	xmlWriter->writeTextElement("IBU_MIN", QString::number(recipe->st_ibu_min, 'f', 0));
	xmlWriter->writeTextElement("IBU_MAX", QString::number(recipe->st_ibu_max, 'f', 0));
	xmlWriter->writeTextElement("COLOR_MIN", QString::number(Utils::ebc_to_srm(recipe->st_color_min), 'f', 2));
	xmlWriter->writeTextElement("COLOR_MAX", QString::number(Utils::ebc_to_srm(recipe->st_color_max), 'f', 2));
	xmlWriter->writeTextElement("CARB_MIN", QString::number(recipe->st_carb_min, 'f', 1));
	xmlWriter->writeTextElement("CARB_MAX", QString::number(recipe->st_carb_max, 'f', 1));
	xmlWriter->writeTextElement("ABV_MIN", QString::number(recipe->st_abv_min, 'f', 1));
	xmlWriter->writeTextElement("ABV_MAX", QString::number(recipe->st_abv_max, 'f', 1));
    xmlWriter->writeEndElement();	// STYLE

    xmlWriter->writeStartElement("EQUIPMENT");
	xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", "Dummy Brewery");
	xmlWriter->writeTextElement("BATCH_SIZE", QString::number(recipe->batch_size, 'f', 2));
	xmlWriter->writeTextElement("BOIL_SIZE", QString::number(recipe->boil_size, 'f', 2));
	xmlWriter->writeTextElement("BOIL_TIME", QString::number(recipe->boil_time, 'f', 0));
    xmlWriter->writeEndElement();	// EQUIPMENT

    xmlWriter->writeStartElement("HOPS");
    for (int i = 0; i < recipe->hops.size(); i++) {
	xmlWriter->writeStartElement("HOP");
	xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", recipe->hops.at(i).name);
	xmlWriter->writeTextElement("ALPHA", QString::number(recipe->hops.at(i).alpha, 'f', 1));
	xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->hops.at(i).amount, 'f', 4));
	xmlWriter->writeTextElement("USE", hop_use[recipe->hops.at(i).useat]);
	xmlWriter->writeTextElement("TIME", QString::number(recipe->hops.at(i).time, 'f', 0));
	xmlWriter->writeTextElement("TYPE", g_hop_types[recipe->hops.at(i).type]);
	xmlWriter->writeTextElement("FORM", hop_forms[recipe->hops.at(i).form]);
	xmlWriter->writeTextElement("BETA", QString::number(recipe->hops.at(i).beta, 'f', 1));
	xmlWriter->writeTextElement("HSI", QString::number(recipe->hops.at(i).hsi, 'f', 1));
	xmlWriter->writeTextElement("ORIGIN", recipe->hops.at(i).origin);
	xmlWriter->writeEndElement();
    }
    xmlWriter->writeEndElement();	// HOPS

    xmlWriter->writeStartElement("FERMENTABLES");
    for (int i = 0; i < recipe->fermentables.size(); i++) {
        xmlWriter->writeStartElement("FERMENTABLE");
        xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", recipe->fermentables.at(i).name);
	xmlWriter->writeTextElement("TYPE", g_fermentable_types[recipe->fermentables.at(i).type]);
	xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->fermentables.at(i).amount, 'f', 4));
	xmlWriter->writeTextElement("YIELD", QString::number(recipe->fermentables.at(i).yield, 'f', 1));
	xmlWriter->writeTextElement("COLOR", QString::number(Utils::ebc_to_srm(recipe->fermentables.at(i).color), 'f', 1));
	xmlWriter->writeTextElement("ADD_AFTER_BOIL", recipe->fermentables.at(i).add_after_boil ? "TRUE":"FALSE");
	xmlWriter->writeTextElement("ORIGIN", recipe->fermentables.at(i).origin);
	xmlWriter->writeTextElement("SUPPLIER", recipe->fermentables.at(i).supplier);
	if (recipe->fermentables.at(i).coarse_fine_diff)
	    xmlWriter->writeTextElement("COARSE_FINE_DIFF", QString::number(recipe->fermentables.at(i).coarse_fine_diff, 'f', 4));
	if (recipe->fermentables.at(i).moisture)
	    xmlWriter->writeTextElement("MOISTURE", QString::number(recipe->fermentables.at(i).moisture, 'f', 4));
	if (recipe->fermentables.at(i).diastatic_power)
	    xmlWriter->writeTextElement("DIASTATIC_POWER", QString::number(recipe->fermentables.at(i).diastatic_power, 'f', 4));
	if (recipe->fermentables.at(i).protein)
	    xmlWriter->writeTextElement("PROTEIN", QString::number(recipe->fermentables.at(i).protein, 'f', 4));
	if (recipe->fermentables.at(i).max_in_batch)
	    xmlWriter->writeTextElement("MAX_IN_BATCH", QString::number(recipe->fermentables.at(i).max_in_batch, 'f', 1));
	xmlWriter->writeTextElement("RECOMMEND_MASH", recipe->fermentables.at(i).recommend_mash ? "TRUE":"FALSE");
	xmlWriter->writeTextElement("GRAINTYPE", g_fermentable_graintypes[recipe->fermentables.at(i).graintype]);
	xmlWriter->writeEndElement();
    }
    xmlWriter->writeEndElement();	// FERMENTABLES

    xmlWriter->writeStartElement("MISCS");
    for (int i = 0; i < recipe->miscs.size(); i++) {
        xmlWriter->writeStartElement("MISC");
        xmlWriter->writeTextElement("VERSION", "1");
        xmlWriter->writeTextElement("NAME", recipe->miscs.at(i).name);
	xmlWriter->writeTextElement("TYPE", g_misc_types[recipe->miscs.at(i).type]);
	xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->miscs.at(i).amount, 'f', 5));
	xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", recipe->miscs.at(i).amount_is_weight ? "TRUE":"FALSE");
	xmlWriter->writeTextElement("USE", g_misc_uses[recipe->miscs.at(i).use_use]);
	xmlWriter->writeTextElement("TIME", QString::number(recipe->miscs.at(i).time, 'f', 0));
	xmlWriter->writeEndElement();
    }
    xmlWriter->writeEndElement();	// MISCS

    xmlWriter->writeStartElement("YEASTS");
    for (int i = 0; i < recipe->yeasts.size(); i++) {
        xmlWriter->writeStartElement("YEAST");
        xmlWriter->writeTextElement("VERSION", "1");
        xmlWriter->writeTextElement("NAME", recipe->yeasts.at(i).name);
	xmlWriter->writeTextElement("TYPE", yeast_type[recipe->yeasts.at(i).type]);
	xmlWriter->writeTextElement("FORM", yeast_form[recipe->yeasts.at(i).form]);
	xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->yeasts.at(i).amount, 'f', 5));
	xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", (recipe->yeasts.at(i).form == 1) ? "TRUE":"FALSE");
	xmlWriter->writeTextElement("LABORATORY", recipe->yeasts.at(i).laboratory);
	xmlWriter->writeTextElement("PRODUCT_ID", recipe->yeasts.at(i).product_id);
	xmlWriter->writeTextElement("MIN_TEMPERATURE", QString::number(recipe->yeasts.at(i).min_temperature, 'f', 1));
	xmlWriter->writeTextElement("MAX_TEMPERATURE", QString::number(recipe->yeasts.at(i).max_temperature, 'f', 1));
	xmlWriter->writeTextElement("ATTENUATION", QString::number(recipe->yeasts.at(i).attenuation, 'f', 1));
	xmlWriter->writeTextElement("ADD_TO_SECONDARY", (recipe->yeasts.at(i).use == 0) ? "FALSE":"TRUE");
	xmlWriter->writeEndElement();
    }
    xmlWriter->writeEndElement();	// YEASTS

    xmlWriter->writeStartElement("WATERS");
    if (recipe->w1_amount > 0) {
	xmlWriter->writeStartElement("WATER");
	xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", recipe->w1_name);
	xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->w1_amount, 'f', 2));
	xmlWriter->writeTextElement("CALCIUM", QString::number(recipe->w1_calcium, 'f', 2));
	xmlWriter->writeTextElement("MAGNESIUM", QString::number(recipe->w1_magnesium, 'f', 2));
	xmlWriter->writeTextElement("BICARBONATE", QString::number(recipe->w1_total_alkalinity * 1.22, 'f', 2));
	xmlWriter->writeTextElement("SULFATE", QString::number(recipe->w1_sulfate, 'f', 2));
	xmlWriter->writeTextElement("CHLORIDE", QString::number(recipe->w1_chloride, 'f', 2));
	xmlWriter->writeTextElement("SODIUM", QString::number(recipe->w1_sodium, 'f', 2));
	xmlWriter->writeTextElement("PH", QString::number(recipe->w1_ph, 'f', 2));
	xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(recipe->w1_total_alkalinity, 'f', 2));
	xmlWriter->writeEndElement();
	if (recipe->w2_amount > 0) {
	    xmlWriter->writeStartElement("WATER");
            xmlWriter->writeTextElement("VERSION", "1");
            xmlWriter->writeTextElement("NAME", recipe->w2_name);
            xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->w2_amount, 'f', 2));
            xmlWriter->writeTextElement("CALCIUM", QString::number(recipe->w2_calcium, 'f', 2));
            xmlWriter->writeTextElement("MAGNESIUM", QString::number(recipe->w2_magnesium, 'f', 2));
            xmlWriter->writeTextElement("BICARBONATE", QString::number(recipe->w2_total_alkalinity * 1.22, 'f', 2));
            xmlWriter->writeTextElement("SULFATE", QString::number(recipe->w2_sulfate, 'f', 2));
            xmlWriter->writeTextElement("CHLORIDE", QString::number(recipe->w2_chloride, 'f', 2));
            xmlWriter->writeTextElement("SODIUM", QString::number(recipe->w2_sodium, 'f', 2));
            xmlWriter->writeTextElement("PH", QString::number(recipe->w2_ph, 'f', 2));
            xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(recipe->w2_total_alkalinity, 'f', 2));
            xmlWriter->writeEndElement();
	}
    }
    xmlWriter->writeEndElement();	// WATERS

    xmlWriter->writeStartElement("MASH");
    xmlWriter->writeTextElement("VERSION", "1");
    xmlWriter->writeTextElement("NAME", recipe->mash_name);
    xmlWriter->writeTextElement("GRAIN_TEMP", "10.0");
    xmlWriter->writeTextElement("PH", QString::number(recipe->sparge_ph, 'f', 2));
    xmlWriter->writeTextElement("SPARGE_TEMP", QString::number(recipe->sparge_temp, 'f', 2));
    xmlWriter->writeStartElement("MASH_STEPS");
    for (int i = 0; i < recipe->mashs.size(); i++) {
	xmlWriter->writeStartElement("MASH_STEP");
	xmlWriter->writeTextElement("VERSION", "1");
	xmlWriter->writeTextElement("NAME", recipe->mashs.at(i).step_name);
	xmlWriter->writeTextElement("TYPE", g_step_types[recipe->mashs.at(i).step_type]);
	if (recipe->mashs.at(i).step_type == 0) {
	    xmlWriter->writeTextElement("INFUSE_AMOUNT", QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 3));
	    xmlWriter->writeTextElement("INFUSE_TEMP", QString::number(recipe->mashs.at(i).step_infuse_temp, 'f', 3));
	}
	if (recipe->mashs.at(i).step_type == 2) {
	    xmlWriter->writeTextElement("DECOCTION_AMT", QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 3));
	}
	xmlWriter->writeTextElement("STEP_TEMP", QString::number(recipe->mashs.at(i).step_temp, 'f', 1));
	xmlWriter->writeTextElement("STEP_TIME", QString::number(recipe->mashs.at(i).step_time, 'f', 1));
	xmlWriter->writeTextElement("RAMP_TIME", QString::number(recipe->mashs.at(i).ramp_time, 'f', 1));
	xmlWriter->writeTextElement("END_TEMP", QString::number(recipe->mashs.at(i).end_temp, 'f', 1));
	xmlWriter->writeTextElement("PH", QString::number(recipe->mash_ph, 'f', 1));
	xmlWriter->writeEndElement();
    }
    xmlWriter->writeEndElement();	// MASH_STEPS
    xmlWriter->writeEndElement();	// MASH
    xmlWriter->writeEndElement();	// RECIPE
    xmlWriter->writeEndElement();	// RECIPES
    xmlWriter->writeEndDocument();
    QMessageBox::information(this, tr("Save File"), tr("XML export ready"));

    file.close();
}


void EditRecipe::copyRecipe()
{
    Recipe *dup = new Recipe;

    dup = recipe;
    dup->record = -1;
    dup->uuid = "";
    dup->name.append(" [duplicate]");
    qDebug() << dup->record << dup->name;
    if (DB_recipe::save(dup, this)) {
	QMessageBox::information(this, tr("Copy Recipe"), tr("Copy Recipe export ready."));
    } else {
	QMessageBox::warning(this, tr("Copy Recipe"), tr("Copy Recipe error."));
    }
    delete dup;
}


void EditRecipe::copyProduct()
{
    Product *p = new Product();

    p->record = -1;
    p->name = recipe->name + QString(" [duplicate]");
    p->code = "";
    p->birth = QDate::currentDate();
    p->stage = p->inventory_reduced = PROD_STAGE_PLAN;
    p->notes = recipe->notes;
//    p->log_brew = p->log_fermentation = p->log_ispindel = p->log_co2pressure = p->locked = false;

    p->st_name = recipe->st_name;
    p->st_letter = recipe->st_letter;
    p->st_guide = recipe->st_guide;
    p->st_category = recipe->st_category;
    p->st_category_number = recipe->st_category_number;
    p->st_type = recipe->st_type;
    p->st_og_min = recipe->st_og_min;
    p->st_og_max = recipe->st_og_max;
    p->st_fg_min = recipe->st_fg_min;
    p->st_fg_max = recipe->st_fg_max;
    p->st_ibu_min = recipe->st_ibu_min;
    p->st_ibu_max = recipe->st_ibu_max;
    p->st_color_min = recipe->st_color_min;
    p->st_color_max = recipe->st_color_max;
    p->st_carb_min = recipe->st_carb_min;
    p->st_carb_max = recipe->st_carb_max;
    p->st_abv_min = recipe->st_abv_min;
    p->st_abv_max = recipe->st_abv_max;

    p->eq_name = QString("Not yet set");
    p->eq_notes = QString("");
    p->eq_tun_specific_heat = 0.11;
//    p->eq_tun_material = 0;
    p->eq_tun_volume = p->eq_tun_height = 20;
    p->eq_tun_weight = 2;
//    p->eq_top_up_water = 0;
    p->eq_trub_chiller_loss = 0.5;
    p->eq_evap_rate = 1.8;
    p->eq_calc_boil_volume = true;
//    p->eq_top_up_kettle = 0;
    p->eq_hop_utilization = 100;
    p->eq_lauter_volume = p->eq_lauter_height = p->eq_kettle_volume = p->eq_kettle_height = p->eq_mash_volume = 20;
    p->eq_lauter_deadspace = 0.5;
    p->eq_mash_max = 6;
    p->eq_efficiency = p->efficiency = recipe->efficiency;
    p->eq_batch_size = p->batch_size = recipe->batch_size;
    p->eq_boil_time = p->boil_time = recipe->boil_time;
    p->eq_boil_size = p->boil_size = p->batch_size + (round(p->batch_size * p->boil_time / 60.0) / 10.0);
    p->type = 2;
    p->color_method = recipe->color_method;
    p->ibu_method = recipe->ibu_method;
    p->est_og = recipe->est_og;
    p->est_fg = recipe->est_fg;
    p->est_color = recipe->est_color;
    p->est_ibu = recipe->est_ibu;
    p->est_abv = recipe->est_abv;

    p->brew_date_start = p->brew_date_end = QDateTime();
//    p->brew_mash_ph = p->brew_mash_sg = p->brew_mash_efficiency = 0;
//    p->brew_sparge_temperature = p->brew_sparge_volume = p->brew_sparge_est = p->brew_sparge_ph = 0;
//    p->brew_preboil_volume = p->brew_preboil_sg = p->brew_preboil_ph = p->brew_preboil_efficiency = 0;
 //   p->brew_aboil_volume = p->brew_aboil_sg = p->brew_aboil_ph = p->brew_aboil_efficiency = 0;
 //   p->brew_cooling_method = p->brew_cooling_time = 0;
    p->brew_cooling_to = 20;
//    p->brew_whirlpool9 = p->brew_whirlpool7 = p->brew_whirlpool6 = p->brew_whirlpool2 = 0;
//    p->brew_fermenter_volume = p->brew_fermenter_extrawater = p->brew_fermenter_tcloss = 0;
//    p->brew_aeration_time = p->brew_aeration_speed = p->brew_aeration_type = 0;
//    p->brew_fermenter_sg = p->brew_fermenter_ibu = p->brew_fermenter_color = 0;

//    p->og = p->fg = 0;
//    p->primary_start_temp = p->primary_max_temp = p->primary_end_temp = p->primary_end_sg = 0;
    p->primary_end_date = p->secondary_end_date = QDate();
//    p->secondary_temp = p->secondary_end_sg = p->tertiary_temp = 0;
    p->package_date = QDate();
//    p->package_volume = p->package_infuse_amount = p->package_infuse_abv = p->package_abv = p->package_ph = 0;
    p->package_infuse_notes = "";
//    p->bottle_amount = p->bottle_carbonation = p->bottle_priming_amount = p->bottle_carbonation_temp = 0;
//    p->keg_amount = p->keg_carbonation = p->keg_priming_amount = p->keg_carbonation_temp = 0;
//    p->keg_pressure = 0;
//    p->bottle_priming_water = p->keg_priming_water = 0;
//    p->bottle_priming_sugar = p->keg_priming_sugar = 0;
//    p->taste_rate = 0;
    p->taste_date = QDate();
    p->taste_notes = p->taste_color = p->taste_transparency = p->taste_head = "";
    p->taste_aroma = p->taste_taste = p->taste_mouthfeel = p->taste_aftertaste = "";

    p->sparge_temp = recipe->sparge_temp;
    p->sparge_ph = recipe->sparge_ph;
    p->sparge_volume = recipe->sparge_volume;
    p->sparge_source = recipe->sparge_source;
    p->sparge_acid_type = recipe->sparge_acid_type;
    p->sparge_acid_perc = recipe->sparge_acid_perc;
    p->sparge_acid_amount = recipe->sparge_acid_amount;
    p->mash_ph = recipe->mash_ph;
    p->mash_name = recipe->mash_name;

    p->calc_acid = recipe->calc_acid;
    p->w1_name = recipe->w1_name;
    p->w1_amount = recipe->w1_amount;
    p->w1_calcium = recipe->w1_calcium;
    p->w1_sulfate = recipe->w1_sulfate;
    p->w1_chloride = recipe->w1_chloride;
    p->w1_sodium = recipe->w1_sodium;
    p->w1_magnesium = recipe->w1_magnesium;
    p->w1_total_alkalinity = recipe->w1_total_alkalinity;
    p->w1_ph = recipe->w1_ph;
    p->w2_name = recipe->w2_name;
    p->w2_amount = recipe->w2_amount;
    p->w2_calcium = recipe->w2_calcium;
    p->w2_sulfate = recipe->w2_sulfate;
    p->w2_chloride = recipe->w2_chloride;
    p->w2_sodium = recipe->w2_sodium;
    p->w2_magnesium = recipe->w2_magnesium;
    p->w2_total_alkalinity = recipe->w2_total_alkalinity;
    p->w2_ph = recipe->w2_ph;
    p->wg_amount = recipe->wg_amount;
    p->wg_calcium = recipe->wg_calcium;
    p->wg_sulfate = recipe->wg_sulfate;
    p->wg_chloride = recipe->wg_chloride;
    p->wg_sodium = recipe->wg_sodium;
    p->wg_magnesium = recipe->wg_magnesium;
    p->wg_total_alkalinity = recipe->wg_total_alkalinity;
    p->wg_ph = recipe->wg_ph;
    p->wb_calcium = recipe->wb_calcium;
    p->wb_sulfate = recipe->wb_sulfate;
    p->wb_chloride = recipe->wb_chloride;
    p->wb_sodium = recipe->wb_sodium;
    p->wb_magnesium = recipe->wb_magnesium;
    p->wb_total_alkalinity = recipe->wb_total_alkalinity;
    p->wb_ph = recipe->wb_ph;
    p->wa_acid_name = recipe->wa_acid_name;
    p->wa_acid_perc = recipe->wa_acid_perc;
    p->wa_base_name = recipe->wa_base_name;

    p->starter_enable = false;
//    p->starter_type = p->prop_type[0] = p->prop_type[1] = p->prop_type[2] = p->prop_type[3] = 0;
    p->starter_viability = 100;
    p->starter_sg = 1.037;
    p->yeast_prod_date = QDate();
//    p->yeast_pitchrate = p->prop_volume[0] = p->prop_volume[1] = p->prop_volume[2] = p->prop_volume[3] = 0;
//    p->divide_type = p->divide_parts = p->divide_part = 0;
//    p->divide_size = 0;
    p->divide_factor = 1;

    p->fermentables = recipe->fermentables;
    p->hops = recipe->hops;
    p->miscs = recipe->miscs;
    p->yeasts = recipe->yeasts;
    p->mashs = recipe->mashs;

    if (DB_product::save(p, this)) {
        QMessageBox::information(this, tr("Copy Recipe"), tr("Copy Recipe to Product ready."));
    } else {
        QMessageBox::warning(this, tr("Copy Recipe"), tr("Copy Recipe to Product error."));
    }
    delete p;
}


void EditRecipe::toforumRecipe()
{
    const QStringList color_method({ "Morey", "Mosher", "Daniels", "Halberstadt", "Naudts" });

    QString memo = QString("[u][b]BMSapp v");
    memo.append(VERSIONSTRING);	// For some stupid reason this must be on it's own.
    memo.append(" - Datum export: " + QDate::currentDate().toString("dd-MMM-yyyy") + "[/b][/u]\n\n\n");
    memo.append("[u][b]Basis[/b][/u]\n[tabular]\n");
    memo.append("[head]Omschrijving[/head][head]Waarde[/head]\n");
    memo.append("[row][data]Bier naam[/data][data]" + recipe->name + "[/data][/row]\n");
    memo.append("[row][data]Bier stijl[/data][data]" + recipe->st_name + "[/data][/row]\n");
    memo.append("[row][data]Recept type[/data][data]" + QCoreApplication::translate("RecipeType", g_recipe_types[recipe->type]) + "[/data][/row]\n");
    memo.append("[row][data]Batch grootte[/data][data]" + QString::number(recipe->batch_size, 'f', 1) + " L[/data][/row]\n");
    memo.append("[row][data]Kooktijd[/data][data]" + QString::number(recipe->boil_time, 'f', 0) + " minuten[/data][/row]\n");
    memo.append("[row][data]Brouwzaal rendement[/data][data]" + QString::number(recipe->efficiency, 'f', 1) + "%[/data][/row]\n");
    memo.append("[row][data]Geschatte begin densiteit[/data][data]" + QString::number(recipe->est_og, 'f', 3) + " SG[/data][/row]\n");
    memo.append("[row][data]Geschatte eind densiteit[/data][data]" + QString::number(recipe->est_fg, 'f', 3) + " SG[/data][/row]\n");
    memo.append("[row][data]Geschat alcohol[/data][data]" + QString::number(recipe->est_abv, 'f', 1) + "%[/data][/row]\n");
    memo.append("[row][data]Kleur (" + color_method[recipe->color_method] + ")[/data][data]" + QString::number(recipe->est_color, 'f', 0) + " EBC[/data][/row]\n");
    memo.append("[row][data]Bitterheid (" + QString(g_ibu_method[recipe->ibu_method]) + ")[/data][data]" + QString::number(recipe->est_ibu, 'f', 1) + " IBU[/data][/row]\n");
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Vergistbare ingrediënten[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Mout, granen en suikers[/head][head]EBC[/head][head]Gewicht kg[/head][head]%[/head][head]Gebruik tijdens[/head]\n");
    for (int i = 0; i < recipe->fermentables.size(); i++) {
	memo.append("[row][data]" + recipe->fermentables.at(i).name + " (" + recipe->fermentables.at(i).supplier + ")[/data]");
	memo.append("[data]" + QString::number(recipe->fermentables.at(i).color) + "[/data]");
	memo.append("[data]" + QString::number(recipe->fermentables.at(i).amount, 'f', 3) + "[/data]");
	memo.append("[data]" + QString::number(recipe->fermentables.at(i).percentage, 'f', 1) + "[/data]");
	memo.append("[data]" + QCoreApplication::translate("FermentableAdded", g_fermentable_added[recipe->fermentables.at(i).added]) + "[/data][/row]\n");
    }
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Hop[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Hop[/head][head]Vorm[/head][head]Alpha[/head][head]IBU[/head][head]Gram[/head][head]Toevoegen moment[/head]\n");
    for (int i = 0; i < recipe->hops.size(); i++) {
	double ibu = Utils::toIBU(recipe->hops.at(i).useat, recipe->hops.at(i).form, recipe->preboil_sg, recipe->est_og, recipe->batch_size,
			recipe->hops.at(i).amount, recipe->hops.at(i).time, recipe->hops.at(i).alpha, recipe->ibu_method,
			0, recipe->hops.at(i).time, 0, recipe->boil_time, 0, 0, 0, recipe->hops.at(i).utilisation, recipe->hops.at(i).bu_factor);
	memo.append("[row][data]" + recipe->hops.at(i).name + " (" + recipe->hops.at(i).origin + ")[/data]");
	memo.append("[data]" + QCoreApplication::translate("HopForm", g_hop_forms[recipe->hops.at(i).form]) + "[/data]");
	memo.append("[data]" + QString::number(recipe->hops.at(i).alpha, 'f', 1) + "[/data]");
	memo.append("[data]" + QString::number(ibu, 'f', 1) + "[/data]");
	memo.append("[data]" + QString::number(recipe->hops.at(i).amount * 1000, 'f', 2) + "[/data]");
	if (recipe->hops.at(i).useat == HOP_USEAT_BOIL || recipe->hops.at(i).useat == HOP_USEAT_WHIRLPOOL)
	    memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + " " + QString::number(recipe->hops.at(i).time) + " minuten[/data][/row]\n");
	else if (recipe->hops.at(i).useat == HOP_USEAT_DRY_HOP)
	    memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + " " + QString::number(recipe->hops.at(i).time / 1440) + " dagen[/data][/row]\n");
	else
	    memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + "[/data][/row]\n");
    }
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Diversen[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Specerij, kruid, brouwzout[/head][head]Type grondstof[/head][head]Gebruik tijdens[/head][head]Hoeveel[/head]\n");
    for (int i = 0; i < recipe->miscs.size(); i++) {
	memo.append("[row][data]" + recipe->miscs.at(i).name + "[/data]");
	memo.append("[data]" + QCoreApplication::translate("MiscType", g_misc_types[recipe->miscs.at(i).type]) + "[/data]");
	if (recipe->miscs.at(i).use_use == MISC_USES_BOIL)
	    memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[recipe->miscs.at(i).use_use]) + " " + QString::number(recipe->miscs.at(i).time) + " min[/data]");
	else
	    memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[recipe->miscs.at(i).use_use]) + "[/data]");
	memo.append("[data]"+QString::number(recipe->miscs.at(i).amount * 1000, 'f', 2)+((recipe->miscs.at(i).amount_is_weight)?" gr":" ml")+"[/data][/row]\n");
    }
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Gist[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Gistlab en code[/head][head]Omschrijving[/head][head]Gebruik[/head][head]Vorm[/head][head]Hoeveel[/head]\n");
    for (int i = 0; i < recipe->yeasts.size(); i++) {
	memo.append("[row][data]" + recipe->yeasts.at(i).laboratory + " " + recipe->yeasts.at(i).product_id + "[/data]");
	memo.append("[data]" + recipe->yeasts.at(i).name + "[/data]");
	memo.append("[data]" + QCoreApplication::translate("YeastUse", g_yeast_use[recipe->yeasts.at(i).use]) + "[/data]");
	memo.append("[data]" + QCoreApplication::translate("YeastForm", g_yeast_forms[recipe->yeasts.at(i).form]) + "[/data]");
	if (recipe->yeasts.at(i).form == YEAST_FORMS_LIQUID)
	    memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount, 'f', 0) + " pak[/data][/row]\n");
	else if (recipe->yeasts.at(i).form == YEAST_FORMS_DRY || recipe->yeasts.at(i).form == YEAST_FORMS_DRIED)
	    memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount * 1000, 'f', 1) + " gr[/data][/row]\n");
	else
	    memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount * 1000, 'f', 0) + " ml[/data][/row]\n");
    }
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Maischen[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Maisch stap[/head][head]Stap type[/head][head]Temperatuur[/head][head]Rust tijd[/head][head]Opwarm tijd[/head]\n");
    for (int i = 0; i < recipe->mashs.size(); i++) {
	memo.append("[row][data]" + recipe->mashs.at(i).step_name + "[/data]");
	if (recipe->mashs.at(i).step_type != 1)
	    memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[recipe->mashs.at(i).step_type])+" "+QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 1) + " L[/data]");
	else
	    memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[recipe->mashs.at(i).step_type]) + "[/data]");
	memo.append("[data]" + QString::number(recipe->mashs.at(i).step_temp, 'f', 1) + " °C[/data]");
	memo.append("[data]" + QString::number(recipe->mashs.at(i).step_time, 'f', 0) + " min[/data]");
	memo.append("[data]" + QString::number(recipe->mashs.at(i).ramp_time, 'f', 0) + " min[/data][/row]\n");
    }
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Brouwwater[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Omschrijving[/head][head]Waarde[/head]\n");
    if (recipe->w2_name != "" && recipe->w2_amount > 0) {
	memo.append("[row][data]Maischwater 1[/data][data]" + recipe->w1_name + " " + QString::number(recipe->w1_amount, 'f', 1) + " Liter[/data][/row]\n");
	memo.append("[row][data]Maischwater 2[/data][data]" + recipe->w2_name + " " + QString::number(recipe->w2_amount, 'f', 1) + " Liter[/data][/row]\n");
    } else {
	memo.append("[row][data]Maischwater[/data][data]" + recipe->w1_name + " " + QString::number(recipe->w1_amount, 'f', 1) + " Liter[/data][/row]\n");
    }
    memo.append("[row][data]Maischwater aanzuren tot[/data][data]" + QString::number(recipe->mash_ph, 'f', 1) + " pH[/data][/row]\n");
    memo.append("[row][data]Spoelwater geschat[/data][data]" + QString::number(recipe->sparge_volume, 'f', 1) + " Liter[/data][/row]\n");
    memo.append("[row][data]Spoelwater temperatuur[/data][data]" + QString::number(recipe->sparge_temp, 'f', 1) + " °C[/data][/row]\n");
    memo.append("[row][data]Spoelwater aanzuren tot[/data][data]" + QString::number(recipe->sparge_ph, 'f', 1) + " pH[/data][/row]\n");
    memo.append("[/tabular]\n\n");

    memo.append("[u][b]Waterprofiel behandeld water[/b][/u]\n");
    memo.append("[tabular]\n");
    memo.append("[head]Ca[/head][head]Mg[/head][head]Na[/head][head]HCO3[/head][head]Cl[/head][head]SO4[/head]\n");
    memo.append("[row][data]" + QString::number(recipe->wb_calcium, 'f', 1) + "[/data]");
    memo.append("[data]" + QString::number(recipe->wb_magnesium, 'f', 1) + "[/data]");
    memo.append("[data]" + QString::number(recipe->wb_sodium, 'f', 1) + "[/data]");
    memo.append("[data]" + QString::number(recipe->wb_total_alkalinity * 61 / 50, 'f', 1) + "[/data]");
    memo.append("[data]" + QString::number(recipe->wb_chloride, 'f', 1) + "[/data]");
    memo.append("[data]" + QString::number(recipe->wb_sulfate, 'f', 1) + "[/data][/row]\n");
    memo.append("[/tabular]\n\n");

    qDebug().noquote() << memo;
    QGuiApplication::clipboard()->setText(memo, QClipboard::Clipboard);
    QGuiApplication::clipboard()->setText(memo, QClipboard::Selection);

    QMessageBox::information(this, tr("Export to forum"), tr("The recipe and all data are copied to the clipboard.\n"
			    "You can \"paste\" this data in the forum screen in your web browser."));
}

void EditRecipe::on_exportButton_clicked()
{
    QDialog* dialog = new QDialog(this);
    dialog->setWindowTitle(tr("Export choices"));
    dialog->setObjectName(QString::fromUtf8("Dialog"));
    dialog->resize(400, 149);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(280, 20, 81, 61));
    buttonBox->setOrientation(Qt::Vertical);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

    QRadioButton *beerxmlButton = new QRadioButton(dialog);
    beerxmlButton->setObjectName(QString::fromUtf8("beerxmlButton"));
    beerxmlButton->setGeometry(QRect(50, 20, 171, 21));
    beerxmlButton->setText(tr("Export to beerXML"));

    QRadioButton *copy_recipeButton = new QRadioButton(dialog);
    copy_recipeButton->setObjectName(QString::fromUtf8("copy_recipeButton"));
    copy_recipeButton->setGeometry(QRect(50, 50, 171, 21));
    copy_recipeButton->setText(tr("Copy to recipe"));

    QRadioButton *copy_productButton = new QRadioButton(dialog);
    copy_productButton->setObjectName(QString::fromUtf8("copy_productButton"));
    copy_productButton->setGeometry(QRect(50, 80, 171, 21));
    copy_productButton->setText(tr("Copy to product"));

    QRadioButton *toforumButton = new QRadioButton(dialog);
    toforumButton->setObjectName(QString::fromUtf8("toforumButton"));
    toforumButton->setGeometry(QRect(50, 110, 171, 21));
    toforumButton->setText(tr("Export to forum"));

    QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
    QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));

    dialog->setModal(true);
    dialog->exec();
    if (dialog->result() == QDialog::Accepted) {
        if (beerxmlButton->isChecked())
            exportBeerXML();
	if (copy_recipeButton->isChecked())
	    copyRecipe();
	if (copy_productButton->isChecked())
	    copyProduct();
	if (toforumButton->isChecked())
	    toforumRecipe();
    }

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


void EditRecipe::on_printButton_clicked()
{
    PrinterDialog(PR_RECIPE, -1, this);
}

mercurial