www/js/prod_divide.js

Wed, 23 Oct 2019 14:20:50 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 23 Oct 2019 14:20:50 +0200
branch
divide
changeset 533
be8691b7d634
parent 525
8bbc5730aaa8
child 602
10b61aacb1c1
permissions
-rw-r--r--

Added the product divide scripts. All parts have all the volumes and weights divided, including the master record. Splitted batches have their own records with the product code formatted as code-n. From this design, we can rebuild the print and checklist.

/*****************************************************************************
 * Copyright (C) 2019
 *
 * 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: '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: 'float' },
   { 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('change', function(event) {
  var index = event.args.index;
  console.log('divide_type:' + 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);
  $('#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