Fri, 29 Jul 2022 13:12:26 +0200
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.
/** * EditProduct.cpp is part of bmsapp. * * Export product. * * 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 EditProduct::exportBeerXML() { qDebug() << "export"; 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" }); // tetra == 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() + "/" + product->code + " " + product->name + ".xml", tr("Files (*.xml)")); if (fileName == 0) { QMessageBox::warning(this, tr("Save File"), tr("No XML file selected.")); return; } 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"); /* * Product basics */ xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->name); if (product->notes != "") xmlWriter->writeTextElement("NOTES", product->notes); xmlWriter->writeTextElement("TYPE", g_recipe_types[product->type]); xmlWriter->writeTextElement("BREWER", "Anonymous"); xmlWriter->writeTextElement("BATCH_SIZE", QString::number(product->batch_size, 'f', 4)); xmlWriter->writeTextElement("BOIL_SIZE", QString::number(product->boil_size, 'f', 4)); xmlWriter->writeTextElement("BOIL_TIME", QString::number(product->boil_time, 'f', 3)); xmlWriter->writeTextElement("EFFICIENCY", QString::number(product->efficiency, 'f', 4)); if (product->og > 0.9) xmlWriter->writeTextElement("OG", QString::number(product->og, 'f', 3)); if (product->fg > 0.9) xmlWriter->writeTextElement("FG", QString::number(product->fg, 'f', 3)); xmlWriter->writeTextElement("EST_OG", QString::number(product->est_og, 'f', 3)); xmlWriter->writeTextElement("EST_FG", QString::number(product->est_fg, 'f', 3)); if (product->est_abv > 0) xmlWriter->writeTextElement("EST_ABV", QString::number(product->est_abv, 'f', 1)); if (product->est_color > 0) { xmlWriter->writeTextElement("EST_COLOR", QString::number(Utils::ebc_to_srm(product->est_color), 'f', 6)); xmlWriter->writeTextElement("COLOR_METHOD", color_method[product->color_method]); } if (product->est_ibu > 0) { xmlWriter->writeTextElement("EST_IBU", QString::number(product->est_ibu, 'f', 1)); xmlWriter->writeTextElement("IBU_METHOD", g_ibu_method[0]); // Only Tinseth } xmlWriter->writeTextElement("BMS_COOLING_TO", QString::number(product->brew_cooling_to, 'f', 1)); xmlWriter->writeStartElement("STYLE"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->st_name); xmlWriter->writeTextElement("CATEGORY", product->st_category); xmlWriter->writeTextElement("CATEGORY_NUMBER", QString::number(product->st_category_number, 'f', 0)); xmlWriter->writeTextElement("STYLE_LETTER", product->st_letter); xmlWriter->writeTextElement("STYLE_GUIDE", product->st_guide); xmlWriter->writeTextElement("TYPE", g_style_types[product->st_type]); xmlWriter->writeTextElement("OG_MIN", QString::number(product->st_og_min, 'f', 3)); xmlWriter->writeTextElement("OG_MAX", QString::number(product->st_og_max, 'f', 3)); xmlWriter->writeTextElement("FG_MIN", QString::number(product->st_fg_min, 'f', 3)); xmlWriter->writeTextElement("FG_MAX", QString::number(product->st_fg_max, 'f', 3)); xmlWriter->writeTextElement("IBU_MIN", QString::number(product->st_ibu_min, 'f', 0)); xmlWriter->writeTextElement("IBU_MAX", QString::number(product->st_ibu_max, 'f', 0)); xmlWriter->writeTextElement("COLOR_MIN", QString::number(Utils::ebc_to_srm(product->st_color_min), 'f', 2)); xmlWriter->writeTextElement("COLOR_MAX", QString::number(Utils::ebc_to_srm(product->st_color_max), 'f', 2)); xmlWriter->writeTextElement("CARB_MIN", QString::number(product->st_carb_min, 'f', 1)); xmlWriter->writeTextElement("CARB_MAX", QString::number(product->st_carb_max, 'f', 1)); xmlWriter->writeTextElement("ABV_MIN", QString::number(product->st_abv_min, 'f', 1)); xmlWriter->writeTextElement("ABV_MAX", QString::number(product->st_abv_max, 'f', 1)); xmlWriter->writeEndElement(); // STYLE xmlWriter->writeStartElement("EQUIPMENT"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->eq_name); xmlWriter->writeTextElement("NOTES", product->eq_notes); xmlWriter->writeTextElement("BATCH_SIZE", QString::number(product->eq_batch_size, 'f', 2)); xmlWriter->writeTextElement("BOIL_SIZE", QString::number(product->eq_boil_size, 'f', 2)); xmlWriter->writeTextElement("BOIL_TIME", QString::number(product->eq_boil_time, 'f', 0)); xmlWriter->writeEndElement(); // EQUIPMENT xmlWriter->writeStartElement("HOPS"); for (int i = 0; i < product->hops.size(); i++) { xmlWriter->writeStartElement("HOP"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->hops.at(i).name); xmlWriter->writeTextElement("ALPHA", QString::number(product->hops.at(i).alpha, 'f', 1)); xmlWriter->writeTextElement("AMOUNT", QString::number(product->hops.at(i).amount, 'f', 4)); xmlWriter->writeTextElement("USE", hop_use[product->hops.at(i).useat]); xmlWriter->writeTextElement("TIME", QString::number(product->hops.at(i).time, 'f', 0)); xmlWriter->writeTextElement("TYPE", g_hop_types[product->hops.at(i).type]); xmlWriter->writeTextElement("FORM", hop_forms[product->hops.at(i).form]); xmlWriter->writeTextElement("BETA", QString::number(product->hops.at(i).beta, 'f', 1)); xmlWriter->writeTextElement("HSI", QString::number(product->hops.at(i).hsi, 'f', 1)); xmlWriter->writeTextElement("ORIGIN", product->hops.at(i).origin); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // HOPS xmlWriter->writeStartElement("FERMENTABLES"); for (int i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) { xmlWriter->writeStartElement("FERMENTABLE"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->fermentables.at(i).name); xmlWriter->writeTextElement("TYPE", g_fermentable_types[product->fermentables.at(i).type]); xmlWriter->writeTextElement("AMOUNT", QString::number(product->fermentables.at(i).amount, 'f', 4)); xmlWriter->writeTextElement("YIELD", QString::number(product->fermentables.at(i).yield, 'f', 1)); xmlWriter->writeTextElement("COLOR", QString::number(Utils::ebc_to_srm(product->fermentables.at(i).color), 'f', 1)); xmlWriter->writeTextElement("ADD_AFTER_BOIL", product->fermentables.at(i).add_after_boil ? "TRUE":"FALSE"); xmlWriter->writeTextElement("ORIGIN", product->fermentables.at(i).origin); xmlWriter->writeTextElement("SUPPLIER", product->fermentables.at(i).supplier); if (product->fermentables.at(i).coarse_fine_diff) xmlWriter->writeTextElement("COARSE_FINE_DIFF", QString::number(product->fermentables.at(i).coarse_fine_diff, 'f', 4)); if (product->fermentables.at(i).moisture) xmlWriter->writeTextElement("MOISTURE", QString::number(product->fermentables.at(i).moisture, 'f', 4)); if (product->fermentables.at(i).diastatic_power) xmlWriter->writeTextElement("DIASTATIC_POWER", QString::number(product->fermentables.at(i).diastatic_power, 'f', 4)); if (product->fermentables.at(i).protein) xmlWriter->writeTextElement("PROTEIN", QString::number(product->fermentables.at(i).protein, 'f', 4)); if (product->fermentables.at(i).max_in_batch) xmlWriter->writeTextElement("MAX_IN_BATCH", QString::number(product->fermentables.at(i).max_in_batch, 'f', 1)); xmlWriter->writeTextElement("RECOMMEND_MASH", product->fermentables.at(i).recommend_mash ? "TRUE":"FALSE"); xmlWriter->writeTextElement("GRAINTYPE", g_fermentable_graintypes[product->fermentables.at(i).graintype]); xmlWriter->writeEndElement(); } } xmlWriter->writeEndElement(); // FERMENTABLES xmlWriter->writeStartElement("MISCS"); for (int i = 0; i < product->miscs.size(); i++) { xmlWriter->writeStartElement("MISC"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->miscs.at(i).name); xmlWriter->writeTextElement("TYPE", g_misc_types[product->miscs.at(i).type]); xmlWriter->writeTextElement("AMOUNT", QString::number(product->miscs.at(i).amount, 'f', 5)); xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", product->miscs.at(i).amount_is_weight ? "TRUE":"FALSE"); xmlWriter->writeTextElement("USE", g_misc_uses[product->miscs.at(i).use_use]); xmlWriter->writeTextElement("TIME", QString::number(product->miscs.at(i).time, 'f', 0)); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // MISCS xmlWriter->writeStartElement("YEASTS"); for (int i = 0; i < product->yeasts.size(); i++) { xmlWriter->writeStartElement("YEAST"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->yeasts.at(i).name); xmlWriter->writeTextElement("TYPE", yeast_type[product->yeasts.at(i).type]); xmlWriter->writeTextElement("FORM", yeast_form[product->yeasts.at(i).form]); xmlWriter->writeTextElement("AMOUNT", QString::number(product->yeasts.at(i).amount, 'f', 5)); xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", (product->yeasts.at(i).form == 1) ? "TRUE":"FALSE"); xmlWriter->writeTextElement("LABORATORY", product->yeasts.at(i).laboratory); xmlWriter->writeTextElement("PRODUCT_ID", product->yeasts.at(i).product_id); xmlWriter->writeTextElement("MIN_TEMPERATURE", QString::number(product->yeasts.at(i).min_temperature, 'f', 1)); xmlWriter->writeTextElement("MAX_TEMPERATURE", QString::number(product->yeasts.at(i).max_temperature, 'f', 1)); xmlWriter->writeTextElement("ATTENUATION", QString::number(product->yeasts.at(i).attenuation, 'f', 1)); xmlWriter->writeTextElement("ADD_TO_SECONDARY", (product->yeasts.at(i).use == 0) ? "FALSE":"TRUE"); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // YEASTS xmlWriter->writeStartElement("WATERS"); if (product->w1_amount > 0) { xmlWriter->writeStartElement("WATER"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->w1_name); xmlWriter->writeTextElement("AMOUNT", QString::number(product->w1_amount, 'f', 2)); xmlWriter->writeTextElement("CALCIUM", QString::number(product->w1_calcium, 'f', 2)); xmlWriter->writeTextElement("MAGNESIUM", QString::number(product->w1_magnesium, 'f', 2)); xmlWriter->writeTextElement("BICARBONATE", QString::number(product->w1_total_alkalinity * 1.22, 'f', 2)); xmlWriter->writeTextElement("SULFATE", QString::number(product->w1_sulfate, 'f', 2)); xmlWriter->writeTextElement("CHLORIDE", QString::number(product->w1_chloride, 'f', 2)); xmlWriter->writeTextElement("SODIUM", QString::number(product->w1_sodium, 'f', 2)); xmlWriter->writeTextElement("PH", QString::number(product->w1_ph, 'f', 2)); xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(product->w1_total_alkalinity, 'f', 2)); xmlWriter->writeEndElement(); if (product->w2_amount > 0) { xmlWriter->writeStartElement("WATER"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->w2_name); xmlWriter->writeTextElement("AMOUNT", QString::number(product->w2_amount, 'f', 2)); xmlWriter->writeTextElement("CALCIUM", QString::number(product->w2_calcium, 'f', 2)); xmlWriter->writeTextElement("MAGNESIUM", QString::number(product->w2_magnesium, 'f', 2)); xmlWriter->writeTextElement("BICARBONATE", QString::number(product->w2_total_alkalinity * 1.22, 'f', 2)); xmlWriter->writeTextElement("SULFATE", QString::number(product->w2_sulfate, 'f', 2)); xmlWriter->writeTextElement("CHLORIDE", QString::number(product->w2_chloride, 'f', 2)); xmlWriter->writeTextElement("SODIUM", QString::number(product->w2_sodium, 'f', 2)); xmlWriter->writeTextElement("PH", QString::number(product->w2_ph, 'f', 2)); xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(product->w2_total_alkalinity, 'f', 2)); xmlWriter->writeEndElement(); } } xmlWriter->writeEndElement(); // WATERS xmlWriter->writeStartElement("MASH"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->mash_name); xmlWriter->writeTextElement("GRAIN_TEMP", "10.0"); xmlWriter->writeTextElement("PH", QString::number(product->sparge_ph, 'f', 2)); xmlWriter->writeTextElement("SPARGE_TEMP", QString::number(product->sparge_temp, 'f', 2)); xmlWriter->writeStartElement("MASH_STEPS"); for (int i = 0; i < product->mashs.size(); i++) { xmlWriter->writeStartElement("MASH_STEP"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", product->mashs.at(i).step_name); xmlWriter->writeTextElement("TYPE", g_step_types[product->mashs.at(i).step_type]); if (product->mashs.at(i).step_type == 0) { xmlWriter->writeTextElement("INFUSE_AMOUNT", QString::number(product->mashs.at(i).step_infuse_amount, 'f', 3)); xmlWriter->writeTextElement("INFUSE_TEMP", QString::number(product->mashs.at(i).step_infuse_temp, 'f', 3)); } if (product->mashs.at(i).step_type == 2) { xmlWriter->writeTextElement("DECOCTION_AMT", QString::number(product->mashs.at(i).step_infuse_amount, 'f', 3)); } xmlWriter->writeTextElement("STEP_TEMP", QString::number(product->mashs.at(i).step_temp, 'f', 1)); xmlWriter->writeTextElement("STEP_TIME", QString::number(product->mashs.at(i).step_time, 'f', 1)); xmlWriter->writeTextElement("RAMP_TIME", QString::number(product->mashs.at(i).ramp_time, 'f', 1)); xmlWriter->writeTextElement("END_TEMP", QString::number(product->mashs.at(i).end_temp, 'f', 1)); xmlWriter->writeTextElement("PH", QString::number(product->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 EditProduct::copyProduct() { Product *dup = new Product; dup = product; dup->record = -1; dup->uuid = ""; dup->name.append(" [duplicate]"); dup->code.append("-[dup]"); /* * Clear data of a previous brew if present and roll back stages. * But leave all ingredients and volumes, that's what we want. */ if (dup->stage > PROD_STAGE_WAIT) { dup->stage = dup->inventory_reduced = PROD_STAGE_WAIT; } dup->birth = QDate::currentDate(); dup->brew_date_start = dup->brew_date_end = QDateTime(); dup->brew_mash_ph = dup->brew_mash_sg = dup->brew_mash_efficiency = 0; dup->brew_sparge_ph = 0; dup->brew_preboil_volume = dup->brew_preboil_sg = dup->brew_preboil_ph = dup->brew_preboil_efficiency = 0; dup->brew_aboil_volume = dup->brew_aboil_sg = dup->brew_aboil_ph = dup->brew_aboil_efficiency = 0; dup->brew_cooling_time = 0; dup->brew_fermenter_volume = 0; dup->brew_fermenter_sg = dup->brew_fermenter_ibu = dup->brew_fermenter_color = 0; dup->og = dup->fg = 0; dup->primary_start_temp = dup->primary_max_temp = dup->primary_end_temp = dup->primary_end_sg = 0; dup->primary_end_date = dup->secondary_end_date = QDate(); dup->secondary_temp = dup->secondary_end_sg = dup->tertiary_temp = 0; dup->package_date = QDate(); dup->package_volume = dup->package_abv = dup->package_ph = 0; dup->bottle_amount = dup->bottle_carbonation = dup->bottle_priming_amount = dup->bottle_carbonation_temp = 0; dup->keg_amount = dup->keg_carbonation = dup->keg_priming_amount = dup->keg_carbonation_temp = 0; dup->bottle_priming_water = dup->keg_priming_water = 0; dup->taste_rate = 0; dup->taste_date = QDate(); dup->taste_notes = dup->taste_color = dup->taste_transparency = dup->taste_head = ""; dup->taste_aroma = dup->taste_taste = dup->taste_mouthfeel = dup->taste_aftertaste = ""; dup->starter_viability = 100; dup->yeast_prod_date = QDate(); dup->divide_type = dup->divide_parts = dup->divide_part = 0; dup->divide_size = 0; dup->divide_factor = 1; qDebug() << dup->record << dup->name; if (DB_product::save(dup, this)) { QMessageBox::information(this, tr("Copy Product"), tr("Copy Product export ready.")); } else { QMessageBox::warning(this, tr("Copy Product"), tr("Copy Product error.")); } delete dup; } void EditProduct::copyRecipe() { Recipe *r = new Recipe; r->record = -1; r->name = product->name + QString(" [duplicate]"); r->notes = product->notes; r->locked = false; r->st_name = product->st_name; r->st_letter = product->st_letter; r->st_guide = product->st_guide; r->st_category = product->st_category; r->st_category_number = product->st_category_number; r->st_og_min = product->st_og_min; r->st_og_max = product->st_og_max; r->st_fg_min = product->st_fg_min; r->st_fg_max = product->st_fg_max; r->st_ibu_min = product->st_ibu_min; r->st_ibu_max = product->st_ibu_max; r->st_color_min = product->st_color_min; r->st_color_max = product->st_color_max; r->st_carb_min = product->st_carb_min; r->st_carb_max = product->st_carb_max; r->st_abv_min = product->st_abv_min; r->st_abv_max = product->st_abv_max; r->type = product->type; r->batch_size = product->batch_size; r->boil_size = product->boil_size; r->boil_time = product->boil_time; r->efficiency = product->efficiency; r->est_og = product->est_og; r->est_fg = product->est_fg; r->est_abv = product->est_abv; r->est_carb = 0; r->est_color = product->est_color; r->color_method = product->color_method; r->est_ibu = product->est_ibu; r->ibu_method = product->ibu_method; r->sparge_temp = product->sparge_temp; r->sparge_volume = product->sparge_volume; r->sparge_ph = product->sparge_ph; r->sparge_acid_type = product->sparge_acid_type; r->sparge_acid_perc = product->sparge_acid_perc; r->sparge_acid_amount = 0; r->mash_ph = product->mash_ph; r->mash_name = product->mash_name; r->calc_acid = product->calc_acid; r->w1_name = product->w1_name; r->w1_amount = product->w1_amount; r->w1_calcium = product->w1_calcium; r->w1_sulfate = product->w1_sulfate; r->w1_chloride = product->w1_chloride; r->w1_sodium = product->w1_sodium; r->w1_magnesium = product->w1_magnesium; r->w1_total_alkalinity = product->w1_total_alkalinity; r->w1_ph = product->w1_ph; r->w2_name = product->w2_name; r->w2_amount = product->w2_amount; r->w2_calcium = product->w2_calcium; r->w2_sulfate = product->w2_sulfate; r->w2_chloride = product->w2_chloride; r->w2_sodium = product->w2_sodium; r->w2_magnesium = product->w2_magnesium; r->w2_total_alkalinity = product->w2_total_alkalinity; r->w2_ph = product->w2_ph; r->wg_amount = product->wg_amount; r->wg_calcium = product->wg_calcium; r->wg_sulfate = product->wg_sulfate; r->wg_chloride = product->wg_chloride; r->wg_sodium = product->wg_sodium; r->wg_magnesium = product->wg_magnesium; r->wg_total_alkalinity = product->wg_total_alkalinity; r->wg_ph = product->wg_ph; r->wb_calcium = product->wb_calcium; r->wb_sulfate = product->wb_sulfate; r->wb_chloride = product->wb_chloride; r->wb_sodium = product->wb_sodium; r->wb_magnesium = product->wb_magnesium; r->wb_total_alkalinity = product->wb_total_alkalinity; r->wb_ph = product->wb_ph; r->wa_acid_name = product->wa_acid_name; r->wa_acid_perc = product->wa_acid_perc; r->wa_base_name = product->wa_base_name; r->fermentables = product->fermentables; r->hops = product->hops; r->miscs = product->miscs; r->yeasts = product->yeasts; r->mashs = product->mashs; if (DB_recipe::save(r, this)) { QMessageBox::information(this, tr("Copy Product"), tr("Copy Product to Recipe ready.")); } else { QMessageBox::warning(this, tr("Copy Product"), tr("Copy Product to Recipe error.")); } delete r; } void EditProduct::toforumProduct() { 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]" + product->name + "[/data][/row]\n"); memo.append("[row][data]Bier stijl[/data][data]" + product->st_name + "[/data][/row]\n"); memo.append("[row][data]Recept type[/data][data]" + QCoreApplication::translate("RecipeType", g_recipe_types[product->type]) + "[/data][/row]\n"); memo.append("[row][data]Batch grootte[/data][data]" + QString::number(product->batch_size, 'f', 1) + " L[/data][/row]\n"); memo.append("[row][data]Kooktijd[/data][data]" + QString::number(product->boil_time, 'f', 0) + " minuten[/data][/row]\n"); memo.append("[row][data]Brouwzaal rendement[/data][data]" + QString::number(product->efficiency, 'f', 1) + "%[/data][/row]\n"); memo.append("[row][data]Geschatte begin densiteit[/data][data]" + QString::number(product->est_og, 'f', 3) + " SG[/data][/row]\n"); memo.append("[row][data]Geschatte eind densiteit[/data][data]" + QString::number(product->est_fg, 'f', 3) + " SG[/data][/row]\n"); memo.append("[row][data]Geschat alcohol[/data][data]" + QString::number(product->est_abv, 'f', 1) + "%[/data][/row]\n"); memo.append("[row][data]Kleur (" + color_method[product->color_method] + ")[/data][data]" + QString::number(product->est_color, 'f', 0) + " EBC[/data][/row]\n"); memo.append("[row][data]Bitterheid (" + QString(g_ibu_method[product->ibu_method]) + ")[/data][data]" + QString::number(product->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 < product->fermentables.size(); i++) { memo.append("[row][data]" + product->fermentables.at(i).name + " (" + product->fermentables.at(i).supplier + ")[/data]"); memo.append("[data]" + QString::number(product->fermentables.at(i).color) + "[/data]"); memo.append("[data]" + QString::number(product->fermentables.at(i).amount, 'f', 3) + "[/data]"); memo.append("[data]" + QString::number(product->fermentables.at(i).percentage, 'f', 1) + "[/data]"); memo.append("[data]" + QCoreApplication::translate("FermentableAdded", g_fermentable_added[product->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 < product->hops.size(); i++) { double ibu = Utils::toIBU(product->hops.at(i).useat, product->hops.at(i).form, product->preboil_sg, product->est_og3, product->batch_size, product->hops.at(i).amount, product->hops.at(i).time, product->hops.at(i).alpha, product->ibu_method, product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time, product->brew_cooling_method, 0, 0, product->hops.at(i).utilisation, product->hops.at(i).bu_factor); memo.append("[row][data]" + product->hops.at(i).name + " (" + product->hops.at(i).origin + ")[/data]"); memo.append("[data]" + QCoreApplication::translate("HopForm", g_hop_forms[product->hops.at(i).form]) + "[/data]"); memo.append("[data]" + QString::number(product->hops.at(i).alpha, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(ibu, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->hops.at(i).amount * 1000, 'f', 2) + "[/data]"); if (product->hops.at(i).useat == HOP_USEAT_BOIL || product->hops.at(i).useat == HOP_USEAT_WHIRLPOOL) memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[product->hops.at(i).useat]) + " " + QString::number(product->hops.at(i).time) + " minuten[/data][/row]\n"); else if (product->hops.at(i).useat == HOP_USEAT_DRY_HOP) memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[product->hops.at(i).useat]) + " " + QString::number(product->hops.at(i).time / 1440) + " dagen[/data][/row]\n"); else memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[product->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 < product->miscs.size(); i++) { memo.append("[row][data]" + product->miscs.at(i).name + "[/data]"); memo.append("[data]" + QCoreApplication::translate("MiscType", g_misc_types[product->miscs.at(i).type]) + "[/data]"); if (product->miscs.at(i).use_use == MISC_USES_BOIL) memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[product->miscs.at(i).use_use]) + " " + QString::number(product->miscs.at(i).time) + " min[/data]"); else memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[product->miscs.at(i).use_use]) + "[/data]"); memo.append("[data]"+QString::number(product->miscs.at(i).amount * 1000, 'f', 2)+((product->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 < product->yeasts.size(); i++) { memo.append("[row][data]" + product->yeasts.at(i).laboratory + " " + product->yeasts.at(i).product_id + "[/data]"); memo.append("[data]" + product->yeasts.at(i).name + "[/data]"); memo.append("[data]" + QCoreApplication::translate("YeastUse", g_yeast_use[product->yeasts.at(i).use]) + "[/data]"); memo.append("[data]" + QCoreApplication::translate("YeastForm", g_yeast_forms[product->yeasts.at(i).form]) + "[/data]"); if (product->yeasts.at(i).form == YEAST_FORMS_LIQUID) memo.append("[data]" + QString::number(product->yeasts.at(i).amount, 'f', 0) + " pak[/data][/row]\n"); else if (product->yeasts.at(i).form == YEAST_FORMS_DRY || product->yeasts.at(i).form == YEAST_FORMS_DRIED) memo.append("[data]" + QString::number(product->yeasts.at(i).amount * 1000, 'f', 1) + " gr[/data][/row]\n"); else memo.append("[data]" + QString::number(product->yeasts.at(i).amount * 1000, 'f', 0) + " ml[/data][/row]\n"); } memo.append("[/tabular]\n"); if (product->starter_enable) { double sv = 0; for (int i = 0; i < 4; i++) { if ((product->prop_volume[i] > 0) && (product->prop_volume[i] > sv)) sv = product->prop_volume[i]; } memo.append("Maak een giststarter van " + QString::number(sv, 'f', 1) + " liter.\n"); } memo.append("\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 < product->mashs.size(); i++) { memo.append("[row][data]" + product->mashs.at(i).step_name + "[/data]"); if (product->mashs.at(i).step_type != 1) memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[product->mashs.at(i).step_type])+" "+QString::number(product->mashs.at(i).step_infuse_amount, 'f', 1) + " L[/data]"); else memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[product->mashs.at(i).step_type]) + "[/data]"); memo.append("[data]" + QString::number(product->mashs.at(i).step_temp, 'f', 1) + " °C[/data]"); memo.append("[data]" + QString::number(product->mashs.at(i).step_time, 'f', 0) + " min[/data]"); memo.append("[data]" + QString::number(product->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 (product->w2_name != "" && product->w2_amount > 0) { memo.append("[row][data]Maischwater 1[/data][data]" + product->w1_name + " " + QString::number(product->w1_amount, 'f', 1) + " Liter[/data][/row]\n"); memo.append("[row][data]Maischwater 2[/data][data]" + product->w2_name + " " + QString::number(product->w2_amount, 'f', 1) + " Liter[/data][/row]\n"); } else { memo.append("[row][data]Maischwater[/data][data]" + product->w1_name + " " + QString::number(product->w1_amount, 'f', 1) + " Liter[/data][/row]\n"); } memo.append("[row][data]Maischwater aanzuren tot[/data][data]" + QString::number(product->mash_ph, 'f', 1) + " pH[/data][/row]\n"); memo.append("[row][data]Spoelwater geschat[/data][data]" + QString::number(product->brew_sparge_est, 'f', 1) + " Liter[/data][/row]\n"); memo.append("[row][data]Spoelwater temperatuur[/data][data]" + QString::number(product->sparge_temp, 'f', 1) + " °C[/data][/row]\n"); memo.append("[row][data]Spoelwater aanzuren tot[/data][data]" + QString::number(product->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(product->wb_calcium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->wb_magnesium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->wb_sodium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->wb_total_alkalinity * 61 / 50, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->wb_chloride, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(product->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 EditProduct::split_show() { double left = vol_availEdit->value(); qDebug() << "split_show" << product->splits.size(); const QSignalBlocker blocker1(splitTable); if (product->divide_type == 0) { split_addButton->setEnabled(false); split_addButton->setToolTip(""); } else { split_addButton->setEnabled(true); split_addButton->setToolTip(tr("Add a splitted batch")); } if (product->splits.size() == 0) { splitatEdit->setEnabled(true); splitatEdit->setToolTip(tr("Choose split moment in the brew process")); split_delButton->setEnabled(false); split_delButton->setToolTip(tr("")); } else { splitatEdit->setEnabled(false); splitatEdit->setToolTip(""); split_delButton->setEnabled(true); split_delButton->setToolTip(tr("Delete the last splitted batch")); } for (int i = 0; i < product->splits.size(); i++) { qDebug() << i << product->splits.at(i).code << product->splits.at(i).name << QString::number(product->splits.at(i).size); QTableWidgetItem *item = new QTableWidgetItem(product->splits.at(i).code); item->setFlags(item->flags() ^ Qt::ItemIsEditable); item->setToolTip(tr("The read-only `product code` of the batch")); splitTable->setItem(i, 0, item); item = new QTableWidgetItem(product->splits.at(i).name); item->setToolTip(tr("Batch name, click to change the name")); splitTable->setItem(i, 1, item); item = new QTableWidgetItem(QString::number(product->splits.at(i).size, 'f', 2)); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); item->setToolTip(tr("Batch size, click to change the volume")); splitTable->setItem(i, 2, item); left -= product->splits.at(i).size; } if (left < 0) left = 0; vol_leftEdit->setValue(left); } void EditProduct::split_add_clicked() { Splits news; int entry = product->splits.size(); qDebug() << "split_add_clicked" << entry; news.name = product->name + QString(" %1").arg(entry + 1); news.code = product->code + QString("-%1").arg(entry + 1); news.size = 0; product->splits.append(news); splitTable->setRowCount(entry + 1); split_show(); } void EditProduct::split_del_clicked() { int last = product->splits.size() - 1; qDebug() << "split_del_clicked" << last; product->splits.removeAt(last); splitTable->setRowCount(last); split_show(); } void EditProduct::split_splitat_changed(int val) { qDebug() << "split_splitat_changed" << val; switch (val) { case 0: vol_availEdit->setValue(0); break; case 1: vol_availEdit->setValue(product->boil_size); break; case 2: vol_availEdit->setValue(product->batch_size); break; case 3: vol_availEdit->setValue(product->brew_fermenter_volume); break; case 4: case 5: vol_availEdit->setValue(round(product->brew_fermenter_volume * 9.2) / 10.0); // Estimate without trub break; case 6: vol_availEdit->setValue(product->package_volume); break; } product->divide_type = val; split_show(); } void EditProduct::split_table_changed(int nRow, int nCol) { QTableWidgetItem *item; qDebug() << "split_table_changed" << nRow << nCol; if (nCol == 1) { /* Split name is changed. */ item = splitTable->item(nRow, nCol); product->splits[nRow].name = item->text(); } if (nCol == 2) { item = splitTable->item(nRow, nCol); double vol = item->text().toDouble(); double room = vol_availEdit->value(); for (int i = 0; i < product->splits.size(); i++) { if (i != nRow) room -= product->splits.at(i).size; } qDebug() << vol << room; if (vol > (room - 1)) { product->splits[nRow].size = room - 1; } else { product->splits[nRow].size = round(vol * 100.0) / 100.0; } } split_show(); } void EditProduct::splitProduct() { bool doit = false; if (this->textIsChanged) { DB_product::save(product, this); this->textIsChanged = false; } QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("Split product")); dialog->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); dialog->setObjectName(QString::fromUtf8("SplitDialog")); dialog->resize(1024, 560); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(30, 510, 961, 32)); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); buttonBox->setCenterButtons(true); QLabel *topLabel = new QLabel(dialog); topLabel->setObjectName(QString::fromUtf8("topLabel")); topLabel->setGeometry(QRect(30, 20, 961, 31)); topLabel->setText(tr("Split product")); QFont font; font.setPointSize(12); font.setBold(true); font.setWeight(75); topLabel->setFont(font); topLabel->setAlignment(Qt::AlignCenter); QLabel *nameLabel = new QLabel(dialog); nameLabel->setObjectName(QString::fromUtf8("nameLabel")); nameLabel->setGeometry(QRect(20, 80, 141, 20)); nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); nameLabel->setText(tr("Product name:")); QLineEdit *nameEdit = new QLineEdit(dialog); nameEdit->setObjectName(QString::fromUtf8("nameEdit")); nameEdit->setGeometry(QRect(170, 80, 481, 23)); nameEdit->setReadOnly(true); nameEdit->setText(product->name); QLabel *codeLabel = new QLabel(dialog); codeLabel->setObjectName(QString::fromUtf8("codeLabel")); codeLabel->setGeometry(QRect(20, 110, 141, 20)); codeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); codeLabel->setText(tr("Product code:")); QLineEdit *codeEdit = new QLineEdit(dialog); codeEdit->setObjectName(QString::fromUtf8("codeEdit")); codeEdit->setGeometry(QRect(170, 110, 113, 23)); codeEdit->setReadOnly(true); codeEdit->setText(product->code); QLabel *vol_availLabel = new QLabel(dialog); vol_availLabel->setObjectName(QString::fromUtf8("vol_availLabel")); vol_availLabel->setGeometry(QRect(20, 140, 141, 20)); vol_availLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); vol_availLabel->setText(tr("Available volume:")); QLabel *stageLabel = new QLabel(dialog); stageLabel->setObjectName(QString::fromUtf8("stageLabel")); stageLabel->setGeometry(QRect(660, 80, 141, 20)); stageLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stageLabel->setText(tr("Current brew stage:")); QLineEdit *stageEdit = new QLineEdit(dialog); stageEdit->setObjectName(QString::fromUtf8("stageEdit")); stageEdit->setGeometry(QRect(810, 80, 113, 23)); stageEdit->setReadOnly(true); stageEdit->setText(QCoreApplication::translate("ProdStages", g_prod_stages[product->stage])); QLabel *splitatLabel = new QLabel(dialog); splitatLabel->setObjectName(QString::fromUtf8("splitatLabel")); splitatLabel->setGeometry(QRect(660, 110, 141, 20)); splitatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); splitatLabel->setText(tr("Split at moment:")); splitatEdit = new QComboBox(dialog); splitatEdit->setObjectName(QString::fromUtf8("splitatEdit")); splitatEdit->setGeometry(QRect(810, 110, 181, 23)); splitatEdit->addItem(tr("Not divided")); splitatEdit->addItem(tr("After mash")); splitatEdit->addItem(tr("After boil")); splitatEdit->addItem(tr("After cooling")); splitatEdit->addItem(tr("After primary")); splitatEdit->addItem(tr("After secondary")); splitatEdit->addItem(tr("After tertiary")); /* Disable items that have been done. */ if (product->stage > PROD_STAGE_BREW) { splitatEdit->setItemData(1, false, Qt::UserRole -1); splitatEdit->setItemData(2, false, Qt::UserRole -1); splitatEdit->setItemData(3, false, Qt::UserRole -1); } if (product->stage > PROD_STAGE_PRIMARY) { splitatEdit->setItemData(4, false, Qt::UserRole -1); } if (product->stage > PROD_STAGE_SECONDARY) { splitatEdit->setItemData(5, false, Qt::UserRole -1); } if (product->stage > PROD_STAGE_TERTIARY) { splitatEdit->setItemData(6, false, Qt::UserRole -1); } QLabel *vol_leftLabel = new QLabel(dialog); vol_leftLabel->setObjectName(QString::fromUtf8("vol_leftLabel")); vol_leftLabel->setGeometry(QRect(660, 140, 141, 20)); vol_leftLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); vol_leftLabel->setText(tr("Volume remaining:")); vol_leftEdit = new QDoubleSpinBox(dialog); vol_leftEdit->setObjectName(QString::fromUtf8("vol_leftEdit")); vol_leftEdit->setGeometry(QRect(810, 140, 111, 24)); vol_leftEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); vol_leftEdit->setReadOnly(true); vol_leftEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); vol_leftEdit->setMaximum(1000000.000000000000000); vol_leftEdit->setSuffix(tr(" L")); vol_availEdit = new QDoubleSpinBox(dialog); vol_availEdit->setObjectName(QString::fromUtf8("vol_availEdit")); vol_availEdit->setGeometry(QRect(170, 140, 111, 24)); vol_availEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); vol_availEdit->setReadOnly(true); vol_availEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); vol_availEdit->setMaximum(1000000.000000000000000); vol_availEdit->setSuffix(tr(" L")); const QStringList labels({tr("Split code"), tr("Split name"), tr("Split volume") }); splitTable = new QTableWidget(dialog); splitTable->setObjectName(QString::fromUtf8("splitTable")); splitTable->setGeometry(QRect(30, 220, 961, 281)); splitTable->setColumnCount(3); splitTable->setColumnWidth(0, 140); /* Split code */ splitTable->setColumnWidth(1, 680); /* Split name */ splitTable->setColumnWidth(2, 116); /* Split volume */ splitTable->setHorizontalHeaderLabels(labels); splitTable->verticalHeader()->hide(); splitTable->setRowCount(0); split_addButton = new QPushButton(dialog); split_addButton->setObjectName(QString::fromUtf8("addButton")); split_addButton->setGeometry(QRect(170, 180, 100, 23)); QIcon icon; icon.addFile(QString::fromUtf8("../resources/icons/silk/add.png"), QSize(), QIcon::Normal, QIcon::Off); split_addButton->setIcon(icon); split_addButton->setText(tr("Add")); split_delButton = new QPushButton(dialog); split_delButton->setObjectName(QString::fromUtf8("delButton")); split_delButton->setGeometry(QRect(660, 180, 100, 23)); QIcon icon2; icon2.addFile(QString::fromUtf8("../resources/icons/silk/delete.png"), QSize(), QIcon::Normal, QIcon::Off); split_delButton->setIcon(icon2); split_delButton->setText(tr("Delete")); split_show(); QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); connect(split_addButton, SIGNAL(clicked()), this, SLOT(split_add_clicked())); connect(split_delButton, SIGNAL(clicked()), this, SLOT(split_del_clicked())); connect(splitatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::split_splitat_changed); connect(splitTable, SIGNAL(cellChanged(int, int)), this, SLOT(split_table_changed(int, int))); dialog->setModal(true); dialog->exec(); if (dialog->result() == QDialog::Accepted) { doit = true; } disconnect(buttonBox, nullptr, nullptr, nullptr); disconnect(splitatEdit, nullptr, nullptr, nullptr); disconnect(splitTable, nullptr, nullptr, nullptr); if (! doit) return; for (int i = 0; i < product->splits.size(); i++) { Product *sp = new Product; if (DB_product::load(sp, this, product->record)) { double factor = round((product->splits.at(i).size / vol_availEdit->value()) * 1000.0) / 1000.0; qInfo() << "Split create part" << i + 1 << "code" << product->splits.at(i).code << "factor" << factor; sp->record = -1; sp->uuid = ""; sp->name = product->splits.at(i).name; sp->code = product->splits.at(i).code; sp->divide_type = product->divide_type; sp->divide_size = product->splits.at(i).size; sp->divide_factor = factor; sp->divide_parts = product->splits.size(); sp->divide_part = i + 1; /* Now adjust the volumes */ sp->brew_sparge_est = sp->brew_sparge_est * factor; sp->brew_preboil_volume = sp->brew_preboil_volume * factor; sp->brew_aboil_volume = sp->brew_aboil_volume * factor; sp->brew_fermenter_volume = sp->brew_fermenter_volume * factor; sp->brew_fermenter_extrawater = sp->brew_fermenter_extrawater * factor; sp->brew_fermenter_tcloss = sp->brew_fermenter_tcloss * factor; sp->package_volume = sp->package_volume * factor; sp->package_infuse_amount = sp->package_infuse_amount * factor; sp->bottle_amount = sp->bottle_amount * factor; sp->bottle_priming_water = sp->bottle_priming_water * factor; sp->keg_amount = sp->keg_amount * factor; sp->keg_priming_water = sp->keg_priming_water * factor; sp->batch_size = sp->batch_size * factor; sp->boil_size = sp->boil_size * factor; sp->sparge_volume = sp->sparge_volume * factor; sp->sparge_acid_amount = sp->sparge_acid_amount * factor; sp->w1_amount = sp->w1_amount * factor; sp->w2_amount = sp->w2_amount * factor; sp->wg_amount = sp->wg_amount * factor; sp->prop_volume[0] = sp->prop_volume[0] * factor; sp->prop_volume[1] = sp->prop_volume[1] * factor; sp->prop_volume[2] = sp->prop_volume[2] * factor; sp->prop_volume[3] = sp->prop_volume[3] * factor; for (int j = 0; j < sp->fermentables.size(); j++) { sp->fermentables[j].amount = round(sp->fermentables.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->hops.size(); j++) { sp->hops[j].amount = round(sp->hops.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->miscs.size(); j++) { sp->miscs[j].amount = round(sp->miscs.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->yeasts.size(); j++) { sp->yeasts[j].amount = round(sp->yeasts.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->mashs.size(); j++) { sp->mashs[j].step_volume = round(sp->mashs.at(j).step_volume * factor * 100) / 100; sp->mashs[j].step_infuse_amount = round(sp->mashs.at(j).step_infuse_amount * factor * 100) / 100; } doit = DB_product::save(sp, this); } delete sp; if (! doit) break; // If errors } if (doit) { // Still ok? Product *sp = new Product; if (DB_product::load(sp, this, product->record)) { double factor = round((vol_leftEdit->value() / vol_availEdit->value()) * 1000.0) / 1000.0; qInfo() << "Split update main" << sp->code << "factor" << factor; sp->divide_type = product->divide_type; sp->divide_size = vol_leftEdit->value(); sp->divide_factor = factor; sp->divide_parts = product->splits.size(); sp->divide_part = 0; /* Now adjust the volumes */ sp->brew_sparge_est = sp->brew_sparge_est * factor; sp->brew_preboil_volume = sp->brew_preboil_volume * factor; sp->brew_aboil_volume = sp->brew_aboil_volume * factor; sp->brew_fermenter_volume = sp->brew_fermenter_volume * factor; sp->brew_fermenter_extrawater = sp->brew_fermenter_extrawater * factor; sp->brew_fermenter_tcloss = sp->brew_fermenter_tcloss * factor; sp->package_volume = sp->package_volume * factor; sp->package_infuse_amount = sp->package_infuse_amount * factor; sp->bottle_amount = sp->bottle_amount * factor; sp->bottle_priming_water = sp->bottle_priming_water * factor; sp->keg_amount = sp->keg_amount * factor; sp->keg_priming_water = sp->keg_priming_water * factor; sp->batch_size = sp->batch_size * factor; sp->boil_size = sp->boil_size * factor; sp->sparge_volume = sp->sparge_volume * factor; sp->sparge_acid_amount = sp->sparge_acid_amount * factor; sp->w1_amount = sp->w1_amount * factor; sp->w2_amount = sp->w2_amount * factor; sp->wg_amount = sp->wg_amount * factor; sp->prop_volume[0] = sp->prop_volume[0] * factor; sp->prop_volume[1] = sp->prop_volume[1] * factor; sp->prop_volume[2] = sp->prop_volume[2] * factor; sp->prop_volume[3] = sp->prop_volume[3] * factor; for (int j = 0; j < sp->fermentables.size(); j++) { sp->fermentables[j].amount = round(sp->fermentables.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->hops.size(); j++) { sp->hops[j].amount = round(sp->hops.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->miscs.size(); j++) { sp->miscs[j].amount = round(sp->miscs.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->yeasts.size(); j++) { sp->yeasts[j].amount = round(sp->yeasts.at(j).amount * factor * 100000) / 100000; } for (int j = 0; j < sp->mashs.size(); j++) { sp->mashs[j].step_volume = round(sp->mashs.at(j).step_volume * factor * 100) / 100; sp->mashs[j].step_infuse_amount = round(sp->mashs.at(j).step_infuse_amount * factor * 100) / 100; } doit = DB_product::save(sp, this); } delete sp; } if (doit) qInfo() << "Split is finished and ok"; else qWarning() << "Split errors were found"; /* Make sure the product editor is closed after splitting */ this->close(); this->setResult(1); } void EditProduct::on_exportButton_clicked() { QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("Export choices")); dialog->setObjectName(QString::fromUtf8("Dialog")); dialog->resize(400, 179); 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 *copproductButton = new QRadioButton(dialog); copproductButton->setObjectName(QString::fromUtf8("copproductButton")); copproductButton->setGeometry(QRect(50, 50, 171, 21)); copproductButton->setText(tr("Copy to product")); QRadioButton *coprecipeButton = new QRadioButton(dialog); coprecipeButton->setObjectName(QString::fromUtf8("coprecipeButton")); coprecipeButton->setGeometry(QRect(50, 80, 171, 21)); coprecipeButton->setText(tr("Copy to recipe")); QRadioButton *toforumButton = new QRadioButton(dialog); toforumButton->setObjectName(QString::fromUtf8("toforumButton")); toforumButton->setGeometry(QRect(50, 110, 171, 21)); toforumButton->setText(tr("Export to forum")); QRadioButton *splitButton = new QRadioButton(dialog); splitButton->setObjectName(QString::fromUtf8("splitButton")); splitButton->setGeometry(QRect(50, 140, 171, 21)); splitButton->setText(tr("Split this batch")); if ((product->divide_type > 0) || (product->stage > PROD_STAGE_TERTIARY)) splitButton->setEnabled(false); // Disable if already splitted or past last possible splitpoint. 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 (copproductButton->isChecked()) copyProduct(); if (coprecipeButton->isChecked()) copyRecipe(); if (toforumButton->isChecked()) toforumProduct(); if (splitButton->isChecked()) splitProduct(); } disconnect(buttonBox, nullptr, nullptr, nullptr); } void EditProduct::on_printButton_clicked() { QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("Printer report")); dialog->setObjectName(QString::fromUtf8("Dialog")); dialog->resize(400, 101); 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 *recipeButton = new QRadioButton(dialog); recipeButton->setObjectName(QString::fromUtf8("recipeButton")); recipeButton->setGeometry(QRect(50, 20, 171, 21)); recipeButton->setText(tr("Print recipe")); QRadioButton *checklistButton = new QRadioButton(dialog); checklistButton->setObjectName(QString::fromUtf8("checklistButton")); checklistButton->setGeometry(QRect(50, 50, 171, 21)); checklistButton->setText(tr("Print checklist")); 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 (checklistButton->isChecked()) PrinterDialog(PR_CHECKLIST, -1, this); if (recipeButton->isChecked()) PrinterDialog(PR_PRODUCT, -1, this); } disconnect(buttonBox, nullptr, nullptr, nullptr); }