Production:->Previous Year Production Comparison 7 Print... '); output.writeStartElement("html"); output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml"); output.writeStartElement("head"); output.writeTextElement("title", TTR("pytdprodcomp", "Previous Year Production Comparison")); output.writeEndElement(); output.writeStartElement("body"); var cdt = new Date(Date.now()); output.writeTextElement("p", cdt.toLocaleDateString(TTR("reports", "en-US")) + " " + cdt.toLocaleTimeString(TTR("reports", "en-US"))); output.writeTextElement("h1", TTR("pytdprodcomp", "Previous Year Production Comparison")); output.writeTextElement("p", TTR("pytdprodcomp", "This report provides an itemized and overall comparison of roasted coffee production for the dates specified with those dates in the previous year. A chart of this data along with percent change and rolling average of the percent change is also produced.")); output.writeStartElement("table"); output.writeAttribute("style", "page-break-after:auto;"); output.writeAttribute("rules", "groups"); output.writeAttribute("cellpadding", "3px"); output.writeStartElement("thead"); output.writeStartElement("tr"); output.writeTextElement("th", "Coffee"); switch(unitBox.currentIndex) { case 0: output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Kg)")); output.writeTextElement("th", TTR("pytdprodcomp", "Current (Kg)")); output.writeTextElement("th", TTR("pytdprodcomp", "Change (Kg)")); break; case 1: output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Lb)")); output.writeTextElement("th", TTR("pytdprodcomp", "Current (Lb)")); output.writeTextElement("th", TTR("pytdprodcomp", "Change (Lb)")); break; } output.writeEndElement(); output.writeEndElement(); output.writeStartElement("tbody"); var query = new QSqlQuery(); query.exec("START TRANSACTION"); var dateRange = dateSelect.currentRange(); var curStartDate = "'"+dateRange[0]+"'"; var curEndDate = "'"+dateRange[dateRange.length - 1]+"'"; query.exec("SELECT "+curStartDate+"::date - interval '1 year', "+curEndDate+"::date - interval '1 year' + interval '1 day', "+curEndDate+"::date + interval '1 day'"); query.next(); curEndDate = "'"+query.value(2)+"'"; var prevStartDate = "'"+query.value(0)+"'"; var prevEndDate = "'"+query.value(1)+"'"; var transaction_filter; var approval_filter; switch(batchType.currentIndex) { case 0: transaction_filter = ""; break; case 1: transaction_filter = " AND transaction_type = 'ROAST'"; break; case 2: transaction_filter = " AND transaction_type = 'SAMPLEROAST'"; break; } switch(approval.currentIndex) { case 0: approval_filter = ""; break; case 1: approval_filter = " AND approval = true"; break; case 2: approval_filter = " AND approval = false"; break; } var q = "CREATE TEMPORARY TABLE previous ON COMMIT DROP AS SELECT roasted_id, sum(roasted_quantity) AS p FROM roasting_log WHERE time > "+prevStartDate+" AND time < "+prevEndDate+" AND roasted_id IS NOT NULL" + transaction_filter + approval_filter + " GROUP BY roasted_id"; query.exec(q); q = "CREATE TEMPORARY TABLE current ON COMMIT DROP AS SELECT roasted_id, sum(roasted_quantity) AS c FROM roasting_log WHERE time > "+curStartDate+" AND time < "+curEndDate+" AND roasted_id IS NOT NULL" + transaction_filter + approval_filter + " GROUP BY roasted_id"; query.exec(q); query.exec("INSERT INTO previous SELECT roasted_id, 0 FROM current WHERE roasted_id NOT IN (SELECT roasted_id FROM previous)"); query.exec("INSERT INTO current SELECT roasted_id, 0 FROM previous WHERE roasted_id NOT IN (SELECT roasted_id FROM current)"); query.exec("CREATE TEMPORARY TABLE comp ON COMMIT DROP AS SELECT previous.roasted_id, p, c FROM previous LEFT OUTER JOIN current ON previous.roasted_id = current.roasted_id"); query.exec("SELECT (SELECT name FROM items WHERE id = roasted_id) AS name, p, c, (c-p) FROM comp WHERE p > 0 OR c > 0 ORDER BY name"); while(query.next()) { output.writeStartElement("tr"); output.writeTextElement("td", query.value(0)); output.writeTextElement("td", (query.value(1) / conversion).toFixed(2)); output.writeTextElement("td", (query.value(2) / conversion).toFixed(2)); output.writeTextElement("td", (query.value(3) / conversion).toFixed(2)); output.writeEndElement(); } output.writeEndElement(); output.writeStartElement("tfoot"); output.writeTextElement("th", TTR("pytdprodcomp", "Totals")); query.exec("SELECT sum(p), sum(c), sum(c-p) FROM comp"); query.next(); output.writeTextElement("td", (query.value(0) / conversion).toFixed(2)); output.writeTextElement("td", (query.value(1) / conversion).toFixed(2)); output.writeTextElement("td", (query.value(2) / conversion).toFixed(2)); output.writeEndElement(); query.exec("ABORT"); output.writeEndElement(); output.writeStartElement("p"); output.writeAttribute("style", "page-break-inside: avoid"); output.writeStartElement("svg"); output.writeAttribute("xmlns", "http://www.w3.org/2000/svg"); output.writeAttribute("width", "7.5in"); output.writeAttribute("height", "6.3in"); output.writeStartElement("g"); output.writeAttribute("transform", "translate(0,470)"); output.writeStartElement("g"); output.writeAttribute("transform", "scale(1,-1)"); output.writeStartElement("line"); output.writeAttribute("x1", "40"); output.writeAttribute("x2", "40"); output.writeAttribute("y1", "0"); output.writeAttribute("y2", "450"); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); output.writeStartElement("line"); output.writeAttribute("x1", "50"); output.writeAttribute("x2", "650"); output.writeAttribute("y1", "-10"); output.writeAttribute("y2", "-10"); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); output.writeStartElement("line"); output.writeAttribute("x1", "660"); output.writeAttribute("x2", "660"); output.writeAttribute("y1", "0"); output.writeAttribute("y2", "450"); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); var q = "SELECT "+curEndDate+"::date - "+curStartDate+"::date"; query.exec(q); query.next(); var days = query.value(0); var dates = new Array(); var curpounds = new Array(); var prevpounds = new Array(); var change = new Array(); var avgchange = new Array(); var i; for(i = 0; i < days; i++) { q = "SELECT "+curStartDate+"::date + "+(i+1); query.exec(q); query.next(); dates[i] = query.value(0); q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+" AND time < '"+dates[i]+"'" + transaction_filter + approval_filter; query.exec(q); if(query.next()) { curpounds[i] = Number(query.value(0)); } else { curpounds[i] = 0; } q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+"::date - '1 year'::interval AND time < '"+dates[i]+"'::date - '1 year'::interval" + transaction_filter + approval_filter; query.exec(q); if(query.next()) { prevpounds[i] = Number(query.value(0)); } else { prevpounds[i] = 0; } if(curpounds[i] > 0) { change[i] = (curpounds[i] - prevpounds[i])/curpounds[i]; } else { if(prevpounds[i] > 0) { change[i] = -1; } else { change[i] = 0; } } if(i > (parseInt(avgField.text)-2)) { var sum = 0; var j; for(j = 0; j < parseInt(avgField.text); j++) { sum += change[i-j]; } avgchange[i] = sum / parseInt(avgField.text); } } // Calculate the domain of the primary y axis. var maxy1 = 0; var increment = 100; // Starting increment is 100 Lb. if(unitBox.currentIndex == 0) { increment = 110; // Starting increment is 50 Kg if Kg is requested. } while(maxy1 < Math.max(curpounds[days - 1], prevpounds[days - 1])) { maxy1 += increment; } // Calculate the number of grid lines and loosen spacing if there are too many. var divisions = maxy1 / increment; while(divisions > 10) { increment *= 2; divisions = maxy1 / increment; } // Draw y axis grid lines. var pos = 0; while(pos <= maxy1) { output.writeStartElement("line"); output.writeAttribute("x1", "35"); output.writeAttribute("x2", "45"); output.writeAttribute("y1", (450/maxy1)*pos); output.writeAttribute("y2", (450/maxy1)*pos); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); pos += increment; } var n = Math.min.apply(Math, change); var m = Math.max.apply(Math, change); var miny2 = 0; var maxy2; if(n < 0) { while(miny2 > n) { miny2 -= 0.1; } maxy2 = miny2; } else { while(miny2 < n) { miny2 += 0.1; } miny2 -= 0.1; maxy2 = miny2; } while(maxy2 < m) { maxy2 += 0.1; } var range = maxy2 - miny2; pos = miny2; while(pos <= maxy2) { output.writeStartElement("line"); output.writeAttribute("x1", "655"); output.writeAttribute("x2", "665"); output.writeAttribute("y1", (pos-miny2)*(4.5/(range*100))*10000); output.writeAttribute("y2", (pos-miny2)*(4.5/(range*100))*10000); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); pos += 0.1; } pos = 0; while(pos < days) { output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*pos)); output.writeAttribute("x2", 50+((600/(days-1))*pos)); output.writeAttribute("y1", "-5"); output.writeAttribute("y2", "-15"); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); pos += 7; } output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*(days-1))); output.writeAttribute("x2", 50+((600/(days-1))*(days-1))); output.writeAttribute("y1", "-5"); output.writeAttribute("y2", "-15"); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); pos = 1; while(pos < days) { output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*(pos - 1))); output.writeAttribute("x2", 50+((600/(days-1))*pos)); output.writeAttribute("y1", (prevpounds[pos-1]/maxy1)*450); output.writeAttribute("y2", (prevpounds[pos]/maxy1)*450); output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;"); output.writeEndElement(); output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*(pos - 1))); output.writeAttribute("x2", 50+((600/(days-1))*pos)); output.writeAttribute("y1", (curpounds[pos-1]/maxy1)*450); output.writeAttribute("y2", (curpounds[pos]/maxy1)*450); output.writeAttribute("style", "stroke:rgb(255,0,0);stroke-width:1;"); output.writeEndElement(); output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*(pos-1))); output.writeAttribute("x2", 50+((600/(days-1))*pos)); output.writeAttribute("y1", (change[pos-1]-miny2)*(4.5/(range*100))*10000); output.writeAttribute("y2", (change[pos]-miny2)*(4.5/(range*100))*10000); output.writeAttribute("style", "stroke:rgb(0,255,0);stroke-width:1;"); output.writeEndElement(); if(pos > (parseInt(avgField.text)-1)) { output.writeStartElement("line"); output.writeAttribute("x1", 50+((600/(days-1))*(pos-1))); output.writeAttribute("x2", 50+((600/(days-1))*pos)); output.writeAttribute("y1", (avgchange[pos-1]-miny2)*(4.5/(range*100))*10000); output.writeAttribute("y2", (avgchange[pos]-miny2)*(4.5/(range*100))*10000); output.writeAttribute("style", "stroke:rgb(0,0,255);stroke-width:1;"); output.writeEndElement(); } pos++; } output.writeEndElement(); i = 0; while(i <= maxy1) { output.writeStartElement("text"); output.writeAttribute("x", "0"); output.writeAttribute("y", -((450/maxy1)*i)+5); output.writeAttribute("font-size", "12"); switch(unitBox.currentIndex) { case 0: output.writeCharacters((i / 2.2).toFixed(0)); break; case 1: default: output.writeCharacters(i); break; } output.writeEndElement(); i += increment; } i = miny2; var stepSize = 0.1; while((maxy2 - miny2) / stepSize > 20) { stepSize += 0.05; } while(i <= maxy2) { output.writeStartElement("text"); output.writeAttribute("x", "670"); output.writeAttribute("y", -((i-miny2)*(4.5/(range*100))*10000)+5); output.writeAttribute("font-size", "12"); output.writeCharacters(Number(i*100).toFixed(0)+"%"); output.writeEndElement(); i += stepSize; } i = 0; while(i <= days-1) { output.writeStartElement("text"); output.writeAttribute("x", 45+((600/(days-1))*i)); output.writeAttribute("y", "20"); output.writeAttribute("font-size", "12"); output.writeAttribute("transform", "rotate(90 "+(45+((600/(days-1))*i))+",20)"); q = "SELECT ('"+dates[i]+"'::date - '1 day'::interval)::date"; query.exec(q); query.next(); var ds = query.value(0); var parts = ds.split("-"); switch(parts[1]) { case '01': ds = TTR("pytdprodcomp", "January "); break; case '02': ds = TTR("pytdprodcomp", "February "); break; case '03': ds = TTR("pytdprodcomp", "March "); break; case '04': ds = TTR("pytdprodcomp", "April "); break; case '05': ds = TTR("pytdprodcomp", "May "); break; case '06': ds = TTR("pytdprodcomp", "June "); break; case '07': ds = TTR("pytdprodcomp", "July "); break; case '08': ds = TTR("pytdprodcomp", "August "); break; case '09': ds = TTR("pytdprodcomp", "September "); break; case '10': ds = TTR("pytdprodcomp", "October "); break; case '11': ds = TTR("pytdprodcomp", "November "); break; case '12': ds = TTR("pytdprodcomp", "December "); break; } ds = ds + Number(parts[2]); output.writeCharacters(ds); output.writeEndElement(); if(i == days-1) { break; } i += 7; if(i > days-1) { i = days - 1; } } query = query.invalidate(); output.writeStartElement("rect"); output.writeAttribute("fill", "rgb(0,0,0)"); output.writeAttribute("x", "45"); output.writeAttribute("y", "110"); output.writeAttribute("width", "24"); output.writeAttribute("height", "12"); output.writeEndElement(); output.writeStartElement("text"); output.writeAttribute("x", "75"); output.writeAttribute("y", "120"); output.writeAttribute("font-size", "12"); switch(unitBox.currentIndex) { case 0: output.writeCharacters(TTR("pytdprodcomp", "Previous Year Kg")); break; case 1: output.writeCharacters(TTR("pytdprodcomp", "Previous Year Lb")); break; } output.writeEndElement(); output.writeStartElement("rect"); output.writeAttribute("fill", "rgb(255,0,0)"); output.writeAttribute("x", "195"); output.writeAttribute("y", "110"); output.writeAttribute("width", "24"); output.writeAttribute("height", "12"); output.writeEndElement(); output.writeStartElement("text"); output.writeAttribute("x", "225"); output.writeAttribute("y", "120"); output.writeAttribute("font-size", "12"); switch(unitBox.currentIndex) { case 0: output.writeCharacters(TTR("pytdprodcomp", "Current Year Kg")); break; case 1: output.writeCharacters(TTR("pytdprodcomp", "Current Year Lb")); break; } output.writeEndElement(); output.writeStartElement("rect"); output.writeAttribute("fill", "rgb(0,255,0)"); output.writeAttribute("x", "345"); output.writeAttribute("y", "110"); output.writeAttribute("width", "24"); output.writeAttribute("height", "12"); output.writeEndElement(); output.writeStartElement("text"); output.writeAttribute("x", "375"); output.writeAttribute("y", "120"); output.writeAttribute("font-size", "12"); output.writeCharacters(TTR("pytdprodcomp", "% Change")); output.writeEndElement(); output.writeStartElement("rect"); output.writeAttribute("fill", "rgb(0,0,255)"); output.writeAttribute("x", "495"); output.writeAttribute("y", "110"); output.writeAttribute("width", "24"); output.writeAttribute("height", "12"); output.writeEndElement(); output.writeStartElement("text"); output.writeAttribute("x", "525"); output.writeAttribute("y", "120"); output.writeAttribute("font-size", "12"); output.writeCharacters(TTR("pytdprodcomp", "Average % Change")); output.writeEndElement(); output.writeEndElement(); output.writeEndElement(); output.writeEndElement(); output.writeEndElement(); output.writeEndElement(); output.writeEndDocument(); view.setContent(buffer); buffer.close(); } refresh(); dateSelect.rangeUpdated.connect(function() { refresh(); }); avgField.editingFinished.connect(function() { refresh(); }); var notifier = Application.subscribe("roastinglogchange"); notifier.notify.connect(function() { refresh(); }); ]]>