123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- <window id="batchWindow">
- <menu name="Batch">
- <item id="new" shortcut="Ctrl+N">New Batch…</item>
- </menu>
- <layout type="horizontal">
- <layout type="vertical">
- <layout type="horizontal">
- <label>Machine:</label>
- <line id="machine" writable="false" />
- <label>Unit:</label>
- <sqldrop id="unit" />
- <stretch />
- </layout>
- <layout type="horizontal">
- <label>Roasted Coffee:</label>
- <sqldrop data="0" display="1" showdata="true" id="roasted">
- <null />
- <query>SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id IN (SELECT item FROM current_items) ORDER BY name</query>
- </sqldrop>
- <stretch />
- </layout>
- <label>Green Coffee:</label>
- <sqltablearray columns="3" id="greens">
- <column name="Coffee" delegate="sql" showdata="true" null="true" nulltext="Delete" nulldata="delete" data="0" display="1">SELECT id, name FROM coffees WHERE quantity <> 0 ORDER BY name</column>
- <column name="Weight" delegate="numeric" />
- <column name="Remaining" />
- </sqltablearray>
- <layout type="horizontal">
- <label>Green Weight:</label>
- <line id="green" writable="false">0.0</line>
- </layout>
- <layout type="horizontal">
- <button name="Load Profile" type="push" id="load" />
- <button name="No Profile" type="push" id="noprofile" />
- </layout>
- <layout type="horizontal">
- <label>Time:</label>
- <line id="time" writable="false" />
- <label>Duration:</label>
- <line id="duration" writable="false" />
- </layout>
- <layout type="horizontal">
- <label>Roasted Weight:</label>
- <line id="roast" validator="numeric">0.0</line>
- <label>Weight Loss:</label>
- <line id="wloss" writable="false" />
- <button type="check" id="approval" name="Approved" />
- </layout>
- <layout type="horizontal">
- <label>Annotation:</label>
- <textarea id="annotation" />
- </layout>
- <layout type="horizontal">
- <button name="Submit" id="submit" type="push" />
- <button name="Save log as target profile" type="check" id="target" />
- </layout>
- </layout>
- <layout type="vertical">
- <label>Connected Scales</label>
- <layout type="vertical" id="scales" />
- <label>Expected Weight Loss</label>
- <line id="lossspec" writable="false" />
- <label>Expected Roasted Weight</label>
- <layout type="horizontal">
- <label>Min:</label>
- <line id="minroastweight" writable="false" />
- </layout>
- <layout type="horizontal">
- <label>Expected:</label>
- <line id="expectedroastweight" writable="false" />
- </layout>
- <layout type="horizontal">
- <label>Max:</label>
- <line id="maxroastweight" writable="false" />
- </layout>
- <label>Specification Details</label>
- <textarea id="specnotes" />
- <stretch />
- </layout>
- </layout>
- <program>
- <![CDATA[
- var unitBox = findChildObject(this, 'unit');
- unitBox.addItem("g");
- unitBox.addItem("Kg");
- unitBox.addItem("oz");
- unitBox.addItem("lb");
- unitBox.currentIndex = (QSettings.value("script/batch_unit", unitBox.findText("lb")));
- unitBox['currentIndexChanged(int)'].connect(function() {
- QSettings.setValue("script/batch_unit", unitBox.currentIndex);
- });
- var machine = findChildObject(this, "machine");
- machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
- var newMenu = findChildObject(this, 'new');
- newMenu.triggered.connect(function() {
- var bwindow = createWindow("batchWindow");
- bwindow.windowTitle = "Typica - [*]New Batch";
- });
- var batch = this;
- var table = findChildObject(this, 'greens');
- var green = findChildObject(this, 'green');
- var model = table.model();
- var lossField = findChildObject(this, 'wloss');
- lossField.maximumWidth = 80;
- var roasted = findChildObject(this, 'roasted');
- var roastwt = findChildObject(this, 'roast');
- roastwt.maximumWidth = 80;
- var scalesLayout = findChildObject(this, 'scales');
- scalesLayout.spacing = 10;
- if(navigationwindow.loggingWindow.scales.length > 0) {
- for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
- var scale = navigationwindow.loggingWindow.scales[i];
- var label = new DragLabel();
- var weighButton = new QPushButton();
- weighButton.text = "Weigh";
- weighButton.clicked.connect(scale.weigh);
- label.updateMeasurement = function(m, u) {
- switch(unitBox.currentIndex) {
- case 0:
- this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
- break;
- case 1:
- this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
- break;
- case 2:
- this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
- break;
- case 3:
- this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
- break;
- }
- };
- scalesLayout.addWidget(label);
- scalesLayout.addWidget(weighButton);
- scale.newMeasurement.connect(function(m, u) {
- label.updateMeasurement(m, u);
- });
- scale.weigh();
- unitBox['currentIndexChanged(int)'].connect(scale.weigh);
- }
- }
- var remainingStock = new Array();
- var query = new QSqlQuery();
- query.exec("SELECT id, quantity, (SELECT conversion FROM lb_bag_conversion WHERE item = id) FROM coffees WHERE quantity <> 0");
- while(query.next()) {
- remainingStock.push({id: query.value(0),
- quantity: query.value(1),
- conversion: query.value(2)});
- }
- query = query.invalidate();
- var convertToPounds = function(w, u) {
- switch(u) {
- case "g":
- return w * 0.0022;
- case "oz":
- return w * 0.0625;
- case "Kg":
- return w * 2.2;
- }
- return w;
- };
- var convertFromPounds = function(w, u) {
- switch(u) {
- case "g":
- return w / 0.0022;
- case "oz":
- return w / 0.0625;
- case "Kg":
- return w / 2.2;
- }
- return w;
- };
- var specnotes = findChildObject(this, 'specnotes');
- specnotes.readOnly = true;
- var lossspec = findChildObject(this, 'lossspec');
- var minfield = findChildObject(this, 'minroastweight');
- var midfield = findChildObject(this, 'expectedroastweight');
- var maxfield = findChildObject(this, 'maxroastweight');
- var minloss = 0;
- var maxloss = 0;
- var expectloss = 0;
- var updateGreenTable = function() {
- var deleteRow = -1;
- /* The combo box delegate updates user data before display data
- and this code is executed before the model update is fully
- complete. Rather than rely on this behavior continuing, we
- check that the display value has also been updated and defer
- row removal until both updates are complete.
- */
- while((deleteRow = table.findData("delete", 0)) > -1) {
- if(table.data(deleteRow, 0, 0) == "Delete") {
- table.removeRow(table.findData("delete", 0));
- } else {
- break;
- }
- }
- green.text = table.columnSum(1, 0);
- table.resizeColumnToContents(0);
- var gid = 0;
- var r = 0;
- while(gid >= 0)
- {
- gid = Number(table.data(r, 0, 32));
- if(isNaN(gid))
- {
- gid = -1;
- break;
- }
- var bagConversion = 1;
- for(var i = 0; i < remainingStock.length; i++)
- {
- if(gid == Number(remainingStock[i].id))
- {
- var displayValue = Number(remainingStock[i].quantity);
- bagConversion = Number(remainingStock[i].conversion);
- if(!isNaN(Number(table.data(r, 1, 0))))
- {
- var change = Number(table.data(r, 1, 0));
- switch(unitBox.currentIndex)
- {
- case 0:
- change = convertToPounds(change, "g");
- break;
- case 1:
- change = convertToPounds(change, "Kg");
- break;
- case 2:
- change = convertToPounds(change, "oz");
- break;
- }
- displayValue -= change;
- }
- var bagCount = (displayValue / bagConversion).toFixed(2);
- switch(unitBox.currentIndex)
- {
- case 0:
- displayValue = convertFromPounds(displayValue, "g");
- break;
- case 1:
- displayValue = convertFromPounds(displayValue, "Kg");
- break;
- case 2:
- displayValue = convertFromPounds(displayValue, "oz");
- break;
- }
- displayValue = "" + displayValue + " (" + bagCount + " bags)";
- if(table.data(r, 2, 0) != displayValue)
- {
- table.setData(r, 2, displayValue, 0);
- table.setData(r, 2, displayValue, 2);
- table.resizeColumnToContents(2);
- }
- }
- }
- r++;
- }
- if(parseFloat(green.text) > 0)
- {
- var expectedLossDesc = "";
- if(lossspec.text.length > 0) {
- if(minloss != expectloss) {
- minfield.text = (-(parseFloat(green.text)) * (maxloss - 1)).toFixed(2);
- maxfield.text = (-(parseFloat(green.text)) * (minloss - 1)).toFixed(2);
- }
- midfield.text = (-(parseFloat(green.text)) * (expectloss - 1)).toFixed(2);
- }
- if(parseFloat(roastwt.text) > 0)
- {
- lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
- }
- else
- {
- lossField.text = "100%";
- }
- }
- };
- model.dataChanged.connect(updateGreenTable);
- unitBox['currentIndexChanged(int)'].connect(updateGreenTable);
- roastwt.textChanged.connect(function() {
- if(parseFloat(green.text) > 0)
- {
- if(parseFloat(roastwt.text) > 0)
- {
- lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
- }
- else
- {
- lossField.text = "100%";
- }
- }
- });
- var profilebutton = findChildObject(this, 'load');
- profilebutton.setEnabled(false);
- roasted['currentIndexChanged(int)'].connect(function() {
- table.clear();
- var query = new QSqlQuery();
- var q = "SELECT EXISTS(SELECT 1 FROM item_files WHERE item = ";
- q = q + roasted.currentData();
- q = q + ")";
- query.exec(q);
- if(query.next())
- {
- if(query.value(0) == 'false')
- {
- profilebutton.setEnabled(false);
- }
- else
- {
- profilebutton.setEnabled(true);
- }
- }
- else
- {
- profilebutton.setEnabled(false);
- }
- var title = "Typica - [*]New Batch (";
- title = title + roasted.currentText;
- title = title + ")";
- batch.windowTitle = title;
- q = "SELECT unroasted_id FROM roasting_log WHERE roasted_id = ";
- q = q + roasted.currentData();
- q = q + " AND time = (SELECT max(time) FROM roasting_log WHERE roasted_id = ";
- q = q + roasted.currentData();
- q = q + ")";
- query.exec(q);
- if(query.next())
- {
- var unroasted_items = sqlToArray(query.value(0));
- var names = [];
- q = "SELECT name FROM items WHERE id = :id AND quantity <> 0";
- query.prepare(q);
- var allInStock = true;
- for(var i = 0; i < unroasted_items.length; i++)
- {
- query.bind("id", unroasted_items[i]);
- query.exec();
- if(query.next())
- {
- names[i] = query.value(0);
- }
- else
- {
- allInStock = false;
- }
- }
- if(allInStock)
- {
- for(var i = 0; i < unroasted_items.length; i++)
- {
- table.setData(i, 0, names[i], 0);
- table.setData(i, 0, unroasted_items[i], 32);
- }
- }
- }
- query.prepare("SELECT loss, tolerance, notes FROM roasting_specification WHERE item = :id1 AND time = (SELECT max(time) FROM roasting_specification WHERE item = :id2)");
- query.bind(":id1", roasted.currentData());
- query.bind(":id2", roasted.currentData());
- query.exec();
- var lossSpecDescription = "";
- if(query.next()) {
- if(query.value(0).length > 0) {
- lossSpecDescription += (Number(query.value(0)) * 100).toFixed(2);
- minloss = Number(query.value(0));
- maxloss = Number(query.value(0));
- expectloss = Number(query.value(0));
- }
- if(query.value(1).length > 0) {
- lossSpecDescription += " +/- " + (Number(query.value(1)) * 100).toFixed(2);
- minloss -= Number(query.value(1));
- maxloss += Number(query.value(1));
- }
- if(lossSpecDescription.length > 0) {
- lossSpecDescription += "%";
- }
- lossspec.text = lossSpecDescription;
- specnotes.plainText = query.value(2);
- } else {
- lossspec.text = "";
- specnotes.plainText = "";
- }
- roastestimate.text = "";
- query = query.invalidate();
- });
- var validateCapacity = function() {
- if(checkCapacity == "true") {
- if(convertToPounds(parseFloat(green.text), unitBox.currentText) > poundsCapacity) {
- return false;
- }
- }
- return true;
- }
- profilebutton.clicked.connect(function() {
- var proceed = false;
- if(validateCapacity()) {
- proceed = true;
- } else {
- proceed = displayWarning("Suspicious Input", "Entered green coffee weight exceeds maximum batch size. Continue?");
- }
- if(proceed) {
- doLoadProfile();
- }
- });
- var doLoadProfile = function() {
- batch.windowModified = true;
- currentBatchInfo = batch;
- query = new QSqlQuery();
- var q = "SELECT files FROM item_files WHERE item = :item AND time = (SELECT max(time) FROM item_files WHERE item = :again)";
- query.prepare(q);
- query.bind(":item", Number(roasted.currentData()));
- query.bind(":again", Number(roasted.currentData()));
- query.exec();
- var graph;
- var log;
- if(query.next())
- {
- var files = query.value(0);
- files = files.replace("{", "(");
- files = files.replace("}", ")");
- q = "SELECT file, name FROM files WHERE id IN ";
- q = q + files;
- q = q + " AND type = 'profile'";
- query.exec(q);
- if(query.next())
- {
- var targetseries = -1;
- var buffer = new QBuffer(query.value(0));
- var pname = query.value(1);
- var input = new XMLInput(buffer, 1);
- graph = findChildObject(navigationwindow.loggingWindow, 'graph');
- log = findChildObject(navigationwindow.loggingWindow, 'log');
- log.clear();
- graph.clear();
- input.newTemperatureColumn.connect(log.setHeaderData);
- input.newTemperatureColumn.connect(function(col, text) {
- if(text == navigationwindow.loggingWindow.targetcolumnname)
- {
- targetseries = col;
- }
- });
- input.newAnnotationColumn.connect(log.setHeaderData);
- input.measure.connect(graph.newMeasurement);
- input.measure.connect(log.newMeasurement);
- input.measure.connect(function(data, series) {
- if(series == targetseries)
- {
- targetDetector.newMeasurement(data);
- }
- });
- var lc;
- input.lastColumn.connect(function(c) {
- lc = c;
- QSettings.setValue("liveColumn", c + 1);
- navigationwindow.loggingWindow.postLoadColumnSetup(c)
- });
- input.annotation.connect(function(note, tcol, ncol) {
- for(var i = tcol; i < ncol; i++) {
- log.newAnnotation(note, i, ncol);
- }
- });
- }
- }
- query = query.invalidate();
- navigationwindow.loggingWindow.windowTitle = "Typica - [*]" + pname;
- navigationwindow.loggingWindow.raise();
- navigationwindow.loggingWindow.activateWindow();
- graph.updatesEnabled = false;
- log.updatesEnabled = false;
- input.input();
- log.updatesEnabled = true;
- graph.updatesEnabled = true;
- log.newAnnotation("End", 1, lc);
- }
- var noprofilebutton = findChildObject(this, 'noprofile');
- noprofilebutton.clicked.connect(function() {
- batch.windowModified = true;
- currentBatchInfo = batch;
- navigationwindow.loggingWindow.raise();
- navigationwindow.loggingWindow.activateWindow();
- });
- var submitbutton = findChildObject(this, 'submit');
- var timefield = findChildObject(this, 'time');
- var notes = findChildObject(this, 'annotation');
- var duration = findChildObject(this, 'duration');
- var approval = findChildObject(this, 'approval');
- approval.checked = true;
- var target = findChildObject(this, 'target');
- submitbutton.clicked.connect(function() {
- checkQuery = new QSqlQuery();
- checkQuery.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
- if(!checkQuery.next())
- {
- checkQuery.prepare("INSERT INTO machine (id, name) VALUES(:id, :name)");
- checkQuery.bind(":id", selectedRoasterID);
- checkQuery.bind(":name", selectedRoasterName);
- checkQuery.exec();
- }
- checkQuery = checkQuery.invalidate();
- var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
- query = new QSqlQuery();
- query.prepare(q);
- query.bind(":name", timefield.text + " " + roasted.currentText);
- query.bindFileData(":data", batch.tempData);
- query.exec();
- query.next();
- var fileno = query.value(0);
- var file = new QFile(batch.tempData);
- file.remove();
- var q2 = "INSERT INTO roasting_log (time, unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_id, roasted_quantity, transaction_type, annotation, machine, duration, approval, humidity, barometric, indoor_air, outdoor_air, files) VALUES(:time, ";
- q2 = q2 + table.columnArray(0, 32);
- q2 = q2 + ", ";
- for(var i = 0; table.data(i, 1, 0).value != ""; i++)
- {
- table.setData(i, 1, convertToPounds(parseFloat(table.data(i, 1, 0)), unitBox.currentText) ,32)
- }
- q2 = q2 + table.columnArray(1, 32);
- q2 = q2 + ", ";
- q2 = q2 + convertToPounds(parseFloat(green.text), unitBox.currentText);
- q2 = q2 + ", ";
- q2 = q2 + roasted.currentData();
- q2 = q2 + ", ";
- q2 = q2 + convertToPounds(parseFloat(roastwt.text), unitBox.currentText);
- q2 = q2 + ", 'ROAST', :annotation, ";
- q2 = q2 + selectedRoasterID;
- q2 = q2 + ", :duration, :approval, NULL, NULL, NULL, NULL, '{";
- q2 = q2 + fileno;
- q2 = q2 + "}')";
- query2 = new QSqlQuery();
- query2.prepare(q2);
- query2.bind(":time", timefield.text);
- query2.bind(":annotation", notes.plainText);
- query2.bind(":duration", duration.text);
- query2.bind(":approval", approval.checked);
- query2.exec();
- query2 = query2.invalidate();
- if(target.checked) {
- var q3 = "INSERT INTO item_files (time, item, files) VALUES(:time, :item, '{";
- q3 = q3 + fileno;
- q3 = q3 + "}')";
- query.prepare(q3);
- query.bind(":time", timefield.text);
- query.bind(":item", roasted.currentData());
- query.exec();
- }
- query = query.invalidate();
- batch.windowModified = false;
- batch.close();
- });
- ]]>
- </program>
- </window>
|