diff -r 9e43b216ccd3 -r d25a1b160dba www/js/prod_divide.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/js/prod_divide.js Fri Oct 18 13:12:46 2019 +0200 @@ -0,0 +1,555 @@ +/***************************************************************************** + * Copyright (C) 2019 + * + * Michiel Broek + * + * 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'); + } + }); + + durl = 'includes/db_divides.php', + + // Prepare the data + dividerec = { + datatype: 'json', + cache: false, + datafields: [ + // From prod_main + { name: 'record', type: 'number' }, + { name: 'divide_from', type: 'string' }, + { name: 'divide_type', type: 'int' }, + { name: 'divide_size', type: 'float' }, + { name: 'name', type: 'string' }, + { name: 'code', type: 'string' } + ], + id: 'record', + url: durl + '?record=' + my_record + }; + + 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 = $('
'); + toolbar.append(container); + container.append(''); + container.append(''); + $('#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'); + + // Record 0, the master data + div = {}; + div.divide_from = dataRecord.uuid; + div.divide_type = dataRecord.divide_type; + div.divide_size = leftover; + div.divide_part = 0; + div.name = dataRecord.name; + div.code = dataRecord.code; + data = 'insert=true&' + $.param(div); + $.ajax({ + dataType: 'json', + url: durl, + cache: false, + data: data, + type: "POST", + success: function (data, status, xhr) { + console.log('insert divides: 0'); + }, + error: function(jqXHR, textStatus, errorThrown) { + console.log('insert divides: ' + textStatus); + } + }); + + rows = $('#splitGrid').jqxGrid('getrows'); + for (i = 0; i < rows.length; i++) { + row = rows[i]; + console.log('split ' + i); + div = {}; + div.divide_from = dataRecord.uuid; + div.divide_type = dataRecord.divide_type; + div.divide_size = row.split_size; + div.divide_part = i + 1; + div.name = row.split_name; + div.code = row.split_code; + data = 'insert=true&' + $.param(div); + $.ajax({ + dataType: 'json', + url: durl, + cache: false, + data: data, + type: "POST", + success: function (data, status, xhr) { + console.log('insert divides: ' + i); + }, + error: function(jqXHR, textStatus, errorThrown) { + console.log('insert divides: ' + textStatus); + } + }); + } + + div = {}; + div.record = dataRecord.record; + div.divide_type = dataRecord.divide_type; + div.divide_size = leftover; + div.divide_parts = i; + data = 'splitit=true&' + $.param(div); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: "POST", + success: function (data, status, xhr) { + console.log('updated products'); + }, + error: function(jqXHR, textStatus, errorThrown) { + console.log('updated products: ' + textStatus); + } + }); + } + window.location.href = my_return; + }); + +});