www/js/prod_divide.js

Wed, 20 May 2020 21:49:09 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 20 May 2020 21:49:09 +0200
changeset 684
ccb9f24d0fe9
parent 645
3b1510050c9b
child 710
9646123ea063
permissions
-rw-r--r--

Report any online status change via websocket broadcast. When starting, load all in memory tables sorted. The websocket status on the web pages has it's own panel. Prepared the menu system for dynamic updates.

/*****************************************************************************
 * 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: '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('select', 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