www/js/prod_divide.js

Sun, 23 May 2021 16:41:35 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 23 May 2021 16:41:35 +0200
changeset 752
f68e3bbc1ada
parent 722
c4457e10d968
child 754
30abc8024afe
permissions
-rw-r--r--

Fermentables, hops, miscs and yeast now have tests against the added moment with the brewing stage. Added to inventory edit rows, delete rows, and pick choices for the moment to add or edit. Some more popups to explain certain blocks.

/*****************************************************************************
 * Copyright (C) 2019-2020
 *
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of BMS
 *
 * This 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 2, or (at your option) any
 * later version.
 *
 * BrewCloud 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 ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/


$(document).ready(function() {

 $('#divide_type').jqxDropDownList({
  theme: theme,
  source: SplitAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });

 // Calculate the volume in the main batch.
 function calcLeftover() {
    rows = $('#splitGrid').jqxGrid('getrows');
    leftover = Round(available, 1);
    for (i = 0; i < rows.length; i++) {
     row = rows[i];
     leftover -= row.split_size;
     console.log('i:' + i + ' split_size:' + row.split_size);
    }
    $('#leftover').val(leftover);
    console.log('calcLeftover():' + leftover);
 }

 // Calculate available volume but ignore the current row.
 function calcRoom(r) {
  var rows, row, i, vol = 0;

  rows = $('#splitGrid').jqxGrid('getrows');
  for (i = 0; i < rows.length; i++) {
   row = rows[i];
   if (i != r)
    vol += row.split_size;
  }
  maxvolume = Round(available - minvolume - vol, 1);
  console.log('calcRoom(' + r + '):' + vol + ' room:' + maxvolume);
 }

 var dataRecord = {},
 i,
 available = 0,
 leftover = 0,
 minvolume = 0,
 maxvolume = 0,
 url = 'includes/db_product.php',

 // Prepare the data
 source = {
  datatype: 'json',
  cache: false,
  datafields: [
   // From prod_main
   { name: 'record', type: 'number' },
   { name: 'uuid', type: 'string' },
   { name: 'name', type: 'string' },
   { name: 'code', type: 'string' },
   { name: 'birth', type: 'string' },
   { name: 'stage', type: 'int' },
   { name: 'notes', type: 'string' },
   { name: 'log_brew', type: 'int' },
   { name: 'log_fermentation', type: 'int' },
   { name: 'log_ispindel', type: 'int' },
   { name: 'log_co2pressure', type: 'int' },
   { name: 'inventory_reduced', type: 'int' },
   { name: 'locked', type: 'int' },
   { name: 'eq_name', type: 'string' },
   { name: 'eq_boil_size', type: 'float' },
   { name: 'eq_batch_size', type: 'float' },
   { name: 'eq_tun_volume', type: 'float' },
   { name: 'eq_tun_weight', type: 'float' },
   { name: 'eq_tun_specific_heat', type: 'float' },
   { name: 'eq_tun_material', type: 'int' },
   { name: 'eq_tun_height', type: 'float' },
   { name: 'eq_top_up_water', type: 'float' },
   { name: 'eq_trub_chiller_loss', type: 'float' },
   { name: 'eq_evap_rate', type: 'float' },
   { name: 'eq_boil_time', type: 'float' },
   { name: 'eq_calc_boil_volume', type: 'int' },
   { name: 'eq_top_up_kettle', type: 'float' },
   { name: 'eq_hop_utilization', type: 'float' },
   { name: 'eq_notes', type: 'string' },
   { name: 'eq_lauter_volume', type: 'float' },
   { name: 'eq_lauter_height', type: 'float' },
   { name: 'eq_lauter_deadspace', type: 'float' },
   { name: 'eq_kettle_volume', type: 'float' },
   { name: 'eq_kettle_height', type: 'float' },
   { name: 'eq_mash_volume', type: 'float' },
   { name: 'eq_mash_max', type: 'float' },
   { name: 'eq_efficiency', type: 'float' },
   { name: 'brew_date_start', type: 'string' },
   { name: 'brew_mash_ph', type: 'float' },
   { name: 'brew_mash_sg', type: 'float' },
   { name: 'brew_mash_efficiency', type: 'float' },
   { name: 'brew_sparge_est', type: 'float' },
   { name: 'brew_sparge_ph', type: 'float' },
   { name: 'brew_preboil_volume', type: 'float' },
   { name: 'brew_preboil_sg', type: 'float' },
   { name: 'brew_preboil_ph', type: 'float' },
   { name: 'brew_preboil_efficiency', type: 'float' },
   { name: 'brew_aboil_volume', type: 'float' },
   { name: 'brew_aboil_sg', type: 'float' },
   { name: 'brew_aboil_ph', type: 'float' },
   { name: 'brew_aboil_efficiency', type: 'float' },
   { name: 'brew_cooling_method', type: 'int' },
   { name: 'brew_cooling_time', type: 'float' },
   { name: 'brew_cooling_to', type: 'float' },
   { name: 'brew_whirlpool9', type: 'float' },
   { name: 'brew_whirlpool7', type: 'float' },
   { name: 'brew_whirlpool6', type: 'float' },
   { name: 'brew_whirlpool2', type: 'float' },
   { name: 'brew_fermenter_volume', type: 'float' },
   { name: 'brew_fermenter_extrawater', type: 'float' },
   { name: 'brew_fermenter_tcloss', type: 'float' },
   { name: 'brew_aeration_time', type: 'float' },
   { name: 'brew_aeration_speed', type: 'float' },
   { name: 'brew_aeration_type', type: 'int' },
   { name: 'brew_fermenter_sg', type: 'float' },
   { name: 'brew_fermenter_ibu', type: 'float' },
   { name: 'brew_fermenter_color', type: 'float' },
   { name: 'brew_date_end', type: 'string' },
   { name: 'og', type: 'float' },
   { name: 'fg', type: 'float' },
   { name: 'primary_start_temp', type: 'float' },
   { name: 'primary_max_temp', type: 'float' },
   { name: 'primary_end_temp', type: 'float' },
   { name: 'primary_end_sg', type: 'float' },
   { name: 'primary_end_date', type: 'string' },
   { name: 'secondary_temp', type: 'float' },
   { name: 'secondary_end_sg', type: 'float' },
   { name: 'secondary_end_date', type: 'string' },
   { name: 'tertiary_temp', type: 'float' },
   { name: 'package_date', type: 'string' },
   { name: 'package_volume', type: 'float' },
   { name: 'package_infuse_amount', type: 'float' },
   { name: 'package_infuse_abv', type: 'float' },
   { name: 'package_infuse_notes', type: 'string' },
   { name: 'package_abv', type: 'float' },
   { name: 'package_ph', type: 'float' },
   { name: 'bottle_amount', type: 'float' },
   { name: 'bottle_carbonation', type: 'float' },
   { name: 'bottle_priming_water', type: 'float' },
   { name: 'bottle_priming_amount', type: 'float' },
   { name: 'bottle_carbonation_temp', type: 'float' },
   { name: 'keg_amount', type: 'float' },
   { name: 'keg_carbonation', type: 'float' },
   { name: 'keg_priming_water', type: 'float' },
   { name: 'keg_priming_amount', type: 'float' },
   { name: 'keg_carbonation_temp', type: 'float' },
   { name: 'keg_forced_carb', type: 'int' },
   { name: 'keg_pressure', type: 'float' },
   { name: 'taste_notes', type: 'string' },
   { name: 'taste_rate', type: 'float' },
   { name: 'taste_date', type: 'string' },
   { name: 'taste_color', type: 'string' },
   { name: 'taste_transparency', type: 'string' },
   { name: 'taste_head', type: 'string' },
   { name: 'taste_aroma', type: 'string' },
   { name: 'taste_taste', type: 'string' },
   { name: 'taste_mouthfeel', type: 'string' },
   { name: 'taste_aftertaste', type: 'string' },
   { name: 'st_name', type: 'string' },
   { name: 'st_letter', type: 'string' },
   { name: 'st_guide', type: 'string' },
   { name: 'st_category', type: 'string' },
   { name: 'st_category_number', type: 'int' },
   { name: 'st_type', type: 'int' },
   { name: 'st_og_min', type: 'float' },
   { name: 'st_og_max', type: 'float' },
   { name: 'st_fg_min', type: 'float' },
   { name: 'st_fg_max', type: 'float' },
   { name: 'st_ibu_min', type: 'float' },
   { name: 'st_ibu_max', type: 'float' },
   { name: 'st_color_min', type: 'float' },
   { name: 'st_color_max', type: 'float' },
   { name: 'st_carb_min', type: 'float' },
   { name: 'st_carb_max', type: 'float' },
   { name: 'st_abv_min', type: 'float' },
   { name: 'st_abv_max', type: 'float' },
   { name: 'type', type: 'int' },
   { name: 'batch_size', type: 'float' },
   { name: 'boil_size', type: 'float' },
   { name: 'boil_time', type: 'float' },
   { name: 'efficiency', type: 'float' },
   { name: 'est_og', type: 'float' },
   { name: 'est_fg', type: 'float' },
   { name: 'est_abv', type: 'float' },
   { name: 'est_color', type: 'float' },
   { name: 'color_method', type: 'int' },
   { name: 'est_ibu', type: 'float' },
   { name: 'ibu_method', type: 'int' },
   { name: 'est_carb', type: 'float' },
   { name: 'sparge_temp', type: 'float' },
   { name: 'sparge_ph', type: 'float' },
   { name: 'sparge_volume', type: 'float' },
   { name: 'sparge_source', type: 'int' },
   { name: 'sparge_acid_type', type: 'int' },
   { name: 'sparge_acid_perc', type: 'float' },
   { name: 'sparge_acid_amount', type: 'float' },
   { name: 'mash_ph', type: 'float' },
   { name: 'mash_name', type: 'string' },
   { name: 'calc_acid', type: 'int' },
   { name: 'w1_name', type: 'string' },
   { name: 'w1_amount', type: 'float' },
   { name: 'w1_calcium', type: 'float' },
   { name: 'w1_sulfate', type: 'float' },
   { name: 'w1_chloride', type: 'float' },
   { name: 'w1_sodium', type: 'float' },
   { name: 'w1_magnesium', type: 'float' },
   { name: 'w1_total_alkalinity', type: 'float' },
   { name: 'w1_ph', type: 'float' },
   { name: 'w1_cost', type: 'float' },
   { name: 'w2_name', type: 'string' },
   { name: 'w2_amount', type: 'float' },
   { name: 'w2_calcium', type: 'float' },
   { name: 'w2_sulfate', type: 'float' },
   { name: 'w2_chloride', type: 'float' },
   { name: 'w2_sodium', type: 'float' },
   { name: 'w2_magnesium', type: 'float' },
   { name: 'w2_total_alkalinity', type: 'float' },
   { name: 'w2_ph', type: 'float' },
   { name: 'w2_cost', type: 'float' },
   { name: 'wg_amount', type: 'float' },
   { name: 'wg_calcium', type: 'float' },
   { name: 'wg_sulfate', type: 'float' },
   { name: 'wg_chloride', type: 'float' },
   { name: 'wg_sodium', type: 'float' },
   { name: 'wg_magnesium', type: 'float' },
   { name: 'wg_total_alkalinity', type: 'float' },
   { name: 'wg_ph', type: 'float' },
   { name: 'wb_calcium', type: 'float' },
   { name: 'wb_sulfate', type: 'float' },
   { name: 'wb_chloride', type: 'float' },
   { name: 'wb_sodium', type: 'float' },
   { name: 'wb_magnesium', type: 'float' },
   { name: 'wb_total_alkalinity', type: 'float' },
   { name: 'wb_ph', type: 'float' },
   { name: 'wa_acid_name', type: 'int' },
   { name: 'wa_acid_perc', type: 'int' },
   { name: 'wa_base_name', type: 'int' },
   { name: 'starter_enable', type: 'int' },
   { name: 'starter_type', type: 'int' },
   { name: 'starter_sg', type: 'float' },
   { name: 'starter_viability', type: 'int' },
   { name: 'starter_viability', type: 'int' },
   { name: 'prop1_type', type: 'int' },
   { name: 'prop1_volume', type: 'float' },
   { name: 'prop2_type', type: 'int' },
   { name: 'prop2_volume', type: 'float' },
   { name: 'prop3_type', type: 'int' },
   { name: 'prop3_volume', type: 'float' },
   { name: 'prop4_type', type: 'int' },
   { name: 'prop4_volume', type: 'float' },
   { name: 'divide_type', type: 'int' },
   { name: 'divide_size', type: 'float' },
   { name: 'divide_parts', type: 'int' },
   { name: 'fermentables', type: 'string' },
   { name: 'hops', type: 'string' },
   { name: 'miscs', type: 'string' },
   { name: 'yeasts', type: 'string' },
   { name: 'mashs', type: 'string' }
  ],
  id: 'record',
  url: url + '?record=' + my_record
 },

 // Load data and select one record.
 dataAdapter = new $.jqx.dataAdapter(source, {
  loadComplete: function() {
   var records = dataAdapter.records;
   dataRecord = records[0];
   // Hidden record uuid
   $('#name').val(dataRecord.name);
   $('#code').val(dataRecord.code);
   $('#stage').val(StageData[dataRecord.stage].nl);
   // Disable stages that are already done.
   for (i = 0; i < SplitData.length; i++) {
    console.log('i:' + i + ' ok:' + SplitData[i].ok + ' stage:' + dataRecord.stage);
    if (SplitData[i].ok < dataRecord.stage)
     $('#divide_type').jqxDropDownList('disableAt', i);
   }
  },
   loadError: function(jqXHR, status, error) {
  },
  beforeLoadComplete: function(records) {
   $('#jqxLoader').jqxLoader('open');
  }
 });

 var editSplit = function(data) {
  var splitSource = {
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'split_code', type: 'string' },
    { name: 'split_name', type: 'string' },
    { name: 'split_size', type: 'float' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    console.log('split addrow ' + rowid);
    commit(true);
   },
   deleterow: function(rowid, commit) {
    console.log('split deleterow ' + rowid);
    commit(true);
   }
  },
  splitAdapter = new $.jqx.dataAdapter(splitSource, {});
  $('#splitGrid').jqxGrid({
   width: 1240,
   height: 375,
   source: splitAdapter,
   editable: true,
   enabletooltips: true,
   selectionmode: 'singlecell',
   editmode: 'click',
   theme: theme,
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    var container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>');
    toolbar.append(container);
    container.append('<input style="float: left; margin-left: 165px;" id="saddrowbutton" type="button" value="Nieuwe splitsing" />');
    container.append('<input style="float: left; margin-left: 565px;" id="sdeleterowbutton" type="button" value="Verwijder splitsing" />');
    $('#saddrowbutton').jqxButton({ template: 'primary', theme: theme, disabled: true, height: 27, width: 150 });
    $('#saddrowbutton').on('click', function() {
     var row = {}, rowscount = $('#splitGrid').jqxGrid('getdatainformation').rowscount;
     row['split_code'] = dataRecord.code + '-' + (rowscount + 1);
     row['split_name'] = dataRecord.name + ' ' + (rowscount + 1);
     row['split_size'] = 0;
     $('#splitGrid').jqxGrid('addrow', null, row);
     $('#sdeleterowbutton').jqxButton({ disabled: false }); // Enable delete
     $('#divide_type').jqxDropDownList({ disabled: true }); // Disable dropdown
    });
    // Delete last added split
    $('#sdeleterowbutton').jqxButton({ template: 'danger', theme: theme, disabled: true, height: 27, width: 150 });
    $('#sdeleterowbutton').on('click', function() {
     var rowscount, id, row;
     rowscount = $('#splitGrid').jqxGrid('getdatainformation').rowscount;
     id = $('#splitGrid').jqxGrid('getrowid', rowscount - 1);
     // First, give back this batch volume.
     row = $('#splitGrid').jqxGrid('getrowdata', id);
     leftover += row.split_size;
     if (leftover > available)
      leftover = available;
     $('#leftover').val(leftover);
     // Then delete the row.
     $('#splitGrid').jqxGrid('deleterow', id);
     if (rowscount == 1) {
      $('#sdeleterowbutton').jqxButton({ disabled: true }); // No more rows
      $('#divide_type').jqxDropDownList({ disabled: false });
     }
    });
   },
   columns: [
    { text: 'Splits code', datafield: 'split_code', width: 120, editable: false },
    { text: 'Splits naam', datafield: 'split_name' },
    { text: 'Splits volume', datafield: 'split_size', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f1', columntype: 'numberinput',
      validation: function(cell, value) {
       if (value < 0 || value > maxvolume) {
         return { result: false, message: 'Volume should be between 0 and ' + maxvolume + ' liter' };
       }
       return true;
      },
      createeditor: function(row, cellvalue, editor) {
       editor.jqxNumberInput({ decimalDigits: 1, digits: 3 });
      }
    }
   ]
  });
  $('#splitGrid').on('cellbeginedit', function(event) {
    var args = event.args;
    calcRoom(args.rowindex); // Make maxvolume available.
  });
  $('#splitGrid').on('cellvaluechanged', function(event) {
    var args = event.args;
    //console.log("cellvaluechanged, Column: " + args.datafield + ", Row: " + (1 + args.rowindex) + ", Value: " + args.value);
    calcLeftover();
  });
 };

 dataAdapter.dataBind();
 editSplit(dataRecord);

 // initialize the input fields.
 $('#name').jqxTooltip({ content: 'De naam voor dit product.' });
 $('#name').jqxInput({ theme: theme, width: 640, height: 23 });
 $('#code').jqxTooltip({ content: 'Product code nummer.' });
 $('#code').jqxInput({ theme: theme, width: 100, height: 23 });
 $('#stage').jqxTooltip({ content: 'De productie fase van dit product.' });
 $('#stage').jqxInput({ theme: theme, width: 100, height: 23 });
 $('#available').jqxNumberInput(Show1dec);
 $('#leftover').jqxNumberInput(Show1dec);
 $('#divide_type').val(0);
 $('#divide_type').on('select', function(event) {
  var index = event.args.index;
  dataRecord.divide_type = index;
  switch (index) {
   case 0:
    available = 0;
    break;
   case 1:
    available = dataRecord.boil_size;
    break;
   case 2:
    available = dataRecord.batch_size;
    break;
   case 3:
    available = dataRecord.brew_fermenter_volume;
    break;
   case 4:
   case 5:
    available = Round(dataRecord.brew_fermenter_volume * 0.92, 1); // Estimate volume without yeast trub
    break;
   case 6:
    available = dataRecord.package_volume;
    break;
  }
  leftover = available;
//  minvolume = Round(0.1 * available, 1);  /* What was the use for this ?? */
  console.log('divide_type:' + index + ' available:' + available);
  $('#available').val(available);
  $('#leftover').val(leftover);
  if (index != 0) {
    $('#saddrowbutton').jqxButton({ disabled: false });
  } else {
    $('#saddrowbutton').jqxButton({ disabled: true });
  }
 });

 $('#Cancel').jqxButton({ template: 'primary', width: '80px', theme: theme });
 $('#Cancel').bind('click', function() {
  window.location.href = my_return;
 });

 $('#Save').jqxButton({ template: 'success', width: '80px', theme: theme });
 $('#Save').bind('click', function() {
  var rows, row, i, div, data;
  if (leftover != available) {
   console.log('Save and there are splits');

   // Send all the info to the database. The server handles the splitting.
   var divide_data = new Array();
   row = {};
   row.name = dataRecord.name;
   row.code = dataRecord.code;
   row.size = Round(leftover, 4);
   row.factor = Round((leftover / available), 4);
   row.part = 0;
   divide_data.push(row);

   rows = $('#splitGrid').jqxGrid('getrows');
   for (i = 0; i < rows.length; i++) {
    row = rows[i];
    console.log('split ' + i);
    div = {};
    div.size = Round(row.split_size, 4);
    div.factor = Round((row.split_size / available), 4);
    div.part = i + 1;
    div.name = row.split_name;
    div.code = row.split_code;
    divide_data.push(div);
   }

   // Send the data to the server
   div = {};
   div.record = dataRecord.record;
   div.divide_type = dataRecord.divide_type;
   div.divide_parts = i;
   div.divide_data = divide_data;
   data = $.param(div);
   $.ajax({
     dataType: 'json',
     url: 'includes/db_divides.php',
     cache: false,
     data: data,
     type: 'POST',
     success: function(data, status, xhr) {
      console.log('insert divides: success');
      window.location.href = my_return;
     },
     error: function(jqXHR, textStatus, errorThrown) {
      console.log('insert divides: ' + textStatus);
     }
   });

  }
 });
});

mercurial