Typica is a free program for professional coffee roasters. https://typica.us
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <window id="useandcostreport">
  2. <reporttitle>Production:->Average Use and Cost by Origin</reporttitle>
  3. <layout type="vertical">
  4. <layout type="horizontal">
  5. <daterange id="dates" initial="23" /><!-- Lifetime -->
  6. <label>Sort Order:</label>
  7. <sqldrop id="sort" />
  8. <label>Weight Unit:</label>
  9. <sqldrop id="unit" />
  10. <stretch />
  11. </layout>
  12. <webview id="report" />
  13. </layout>
  14. <menu name="File">
  15. <item id="print" shortcut="Ctrl+P">Print...</item>
  16. </menu>
  17. <menu name="Reports" type="reports" src="Reports" />
  18. <program>
  19. <![CDATA[
  20. this.windowTitle = TTR("useandcostreport", "Typica - Average Use and Cost by Origin");
  21. var report = findChildObject(this, 'report');
  22. var printMenu = findChildObject(this, 'print');
  23. printMenu.triggered.connect(function() {
  24. report.print();
  25. });
  26. var sortBox = findChildObject(this, 'sort');
  27. sortBox.addItem(TTR("useandcostreport","Origin A-Z"));
  28. sortBox.addItem(TTR("useandcostreport", "Origin Z-A"));
  29. sortBox.addItem(TTR("useandcostreport", "Avg. Rate Ascending"));
  30. sortBox.addItem(TTR("useandcostreport", "Avg. Rate Descending"));
  31. sortBox.addItem(TTR("useandcostreport", "Avg. Cost Ascending"));
  32. sortBox.addItem(TTR("useandcostreport", "Avg. Cost Descending"));
  33. sortBox.currentIndex = QSettings.value("auco_sort", 0);
  34. var unitBox = findChildObject(this, 'unit');
  35. unitBox.addItem(TTR("useandcostreport", "Kg"));
  36. unitBox.addItem(TTR("useandcostreport", "Lb"));
  37. unitBox.currentIndex = QSettings.value("script/report_unit", 1);
  38. unitBox['currentIndexChanged(int)'].connect(function() {
  39. QSettings.setValue("script/report_unit", unitBox.currentIndex);
  40. refresh();
  41. });
  42. var dateSelect = findChildObject(this, 'dates');
  43. var dateQuery = new QSqlQuery;
  44. dateQuery.exec("SELECT time::date FROM transactions WHERE time = (SELECT min(time) FROM transactions) OR time = (SELECT max(time) FROM transactions) ORDER BY time ASC");
  45. dateQuery.next();
  46. var lifetimeStartDate = dateQuery.value(0);
  47. var lifetimeEndDate;
  48. if(dateQuery.next()) {
  49. lifetimeEndDate = dateQuery.value(0);
  50. } else {
  51. lifetimeEndDate = lifetimeStartDate;
  52. }
  53. dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
  54. dateQuery = dateQuery.invalidate();
  55. dateSelect.rangeUpdated.connect(refresh);
  56. var rowData = new Array();
  57. var rowIndex;
  58. function refresh() {
  59. rowData.length = 0;
  60. var dateRange = dateSelect.currentRange();
  61. var startDate = dateRange[0];
  62. var endDate = dateRange[dateRange.length - 1];
  63. rowIndex = 0;
  64. var buffer = new QBuffer;
  65. buffer.open(3);
  66. var output = new XmlWriter(buffer);
  67. output.writeStartDocument("1.0");
  68. output.writeDTD('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg.dtd">');
  69. output.writeStartElement("html");
  70. output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
  71. output.writeStartElement("head");
  72. output.writeTextElement("title", TTR("useandcostreport", "Recent Use and Cost by Origin"));
  73. output.writeEndElement();
  74. output.writeStartElement("body");
  75. var cdt = new Date(Date.now());
  76. output.writeTextElement("p", cdt.toLocaleDateString(TTR("reports", "en-US")) + " " + cdt.toLocaleTimeString(TTR("reports", "en-US")));
  77. output.writeTextElement("h1", TTR("useandcostreport", "Average Use and Cost by Origin ") + startDate + " - " + endDate);
  78. switch(unitBox.currentIndex)
  79. {
  80. case 0:
  81. output.writeTextElement("p", TTR("useandcostreport", "This is a report of average rate of use in kilograms per day and cost of unroasted coffee."));
  82. break;
  83. case 1:
  84. output.writeTextElement("p", TTR("useandcostreport", "This is a report of average rate of use in pounds per day and cost of unroasted coffee."));
  85. break;
  86. }
  87. output.writeStartElement("table");
  88. output.writeAttribute("rules", "groups");
  89. output.writeAttribute("cellpadding", "3px");
  90. output.writeStartElement("thead");
  91. output.writeStartElement("tr");
  92. output.writeStartElement("th");
  93. output.writeAttribute("colspan", "9");
  94. output.writeCharacters(TTR("useandcostreport", "Regular Coffees"));
  95. output.writeEndElement();
  96. output.writeEndElement();
  97. output.writeStartElement("tr");
  98. output.writeTextElement("th", TTR("useandcostreport", "Origin"));
  99. output.writeTextElement("th", TTR("useandcostreport", "Avg. Rate"));
  100. output.writeTextElement("th", TTR("useandcostreport", "Avg. Cost"));
  101. output.writeTextElement("th", TTR("useandcostreport", "Last Cost"));
  102. output.writeTextElement("th", TTR("useandcostreport", "Last Purchase Date"));
  103. output.writeTextElement("th", TTR("useandcostreport", "Bag Size (min)"));
  104. output.writeTextElement("th", TTR("useandcostreport", "Bag Size (max)"));
  105. output.writeTextElement("th", TTR("useandcostreport", "Bag Size (mean)"));
  106. output.writeTextElement("th", TTR("useandcostreport", "Purchases"));
  107. output.writeEndElement();
  108. output.writeEndElement();
  109. output.writeStartElement("tbody");
  110. var query = new QSqlQuery();
  111. var conversion = 1;
  112. if(unitBox.currentIndex == 0) {
  113. conversion = 2.2;
  114. }
  115. var orderClause;
  116. switch(sortBox.currentIndex)
  117. {
  118. case 0:
  119. orderClause = "origin ASC";
  120. break;
  121. case 1:
  122. orderClause = "origin DESC";
  123. break;
  124. case 2:
  125. orderClause = "rate ASC";
  126. break;
  127. case 3:
  128. orderClause = "rate DESC";
  129. break;
  130. case 4:
  131. orderClause = "cost ASC";
  132. break;
  133. case 5:
  134. orderClause = "cost DESC";
  135. break;
  136. }
  137. query.prepare("WITH q AS (SELECT id, origin, rate/:c1 AS rate, (SELECT cost*:c2 FROM purchase WHERE item = id) AS cost, (SELECT min(time) FROM purchase WHERE item = id) AS purchase_time, (SELECT conversion/:c3 FROM lb_bag_conversion WHERE item = id) AS bag_weight FROM coffee_history WHERE id IN (SELECT id FROM regular_coffees) AND id IN (SELECT item FROM transactions WHERE time >= :startDate AND time < :endDate::date + interval '1 day')) SELECT DISTINCT origin, avg(rate)::numeric(10,2) AS rate, avg(cost)::numeric(10,2) AS cost, (SELECT (cost*:c4)::numeric(10,2) FROM purchase WHERE item = max(q.id)) AS last_cost, max(purchase_time)::date AS last_purchase, min(bag_weight)::numeric(10,2) AS min_weight, max(bag_weight)::numeric(10,2) AS max_weight, avg(bag_weight)::numeric(10,2) AS mean_weight, count(1) AS n FROM q GROUP BY origin ORDER BY " + orderClause);
  138. query.bind(":c1", conversion);
  139. query.bind(":c2", conversion);
  140. query.bind(":c3", conversion);
  141. query.bind(":c4", conversion);
  142. query.bind(":startDate", startDate);
  143. query.bind(":endDate", endDate);
  144. query.exec();
  145. while(query.next())
  146. {
  147. output.writeStartElement("tr");
  148. output.writeAttribute("id", "r" + rowIndex);
  149. output.writeStartElement("td");
  150. output.writeStartElement("a");
  151. output.writeAttribute("href", "typica://script/r" + rowIndex);
  152. rowIndex++;
  153. rowData.push(query.value(0));
  154. output.writeCharacters(sanitize(query.value(0)));
  155. output.writeEndElement();
  156. output.writeEndElement();
  157. for(var i = 1; i < 9; i++) {
  158. output.writeTextElement("td", query.value(i));
  159. }
  160. output.writeEndElement();
  161. }
  162. output.writeStartElement("tr");
  163. output.writeStartElement("th");
  164. output.writeAttribute("colspan", "9");
  165. output.writeCharacters(TTR("useandcostreport", "Decaffeinated Coffees"));
  166. output.writeEndElement();
  167. output.writeEndElement();
  168. query.prepare("WITH q AS (SELECT id, origin, rate/:c1 AS rate, (SELECT cost*:c2 FROM purchase WHERE item = id) AS cost, (SELECT min(time) FROM purchase WHERE item = id) AS purchase_time, (SELECT conversion/:c3 FROM lb_bag_conversion WHERE item = id) AS bag_weight FROM coffee_history WHERE id IN (SELECT id FROM decaf_coffees) AND id IN (SELECT item FROM transactions WHERE time >= :startDate AND time < :endDate::date + interval '1 day')) SELECT DISTINCT origin, avg(rate)::numeric(10,2) AS rate, avg(cost)::numeric(10,2) AS cost, (SELECT (cost*:c4)::numeric(10,2) FROM purchase WHERE item = max(q.id)) AS last_cost, max(purchase_time)::date AS last_purchase, min(bag_weight)::numeric(10,2) AS min_weight, max(bag_weight)::numeric(10,2) AS max_weight, avg(bag_weight)::numeric(10,2) AS mean_weight, count(1) AS n FROM q GROUP BY origin ORDER BY " + orderClause);
  169. query.bind(":c1", conversion);
  170. query.bind(":c2", conversion);
  171. query.bind(":c3", conversion);
  172. query.bind(":c4", conversion);
  173. query.bind(":startDate", startDate);
  174. query.bind(":endDate", endDate);
  175. query.exec();
  176. while(query.next())
  177. {
  178. output.writeStartElement("tr");
  179. output.writeAttribute("id", "d" + rowIndex);
  180. output.writeStartElement("td");
  181. output.writeStartElement("a");
  182. output.writeAttribute("href", "typica://script/d" + rowIndex);
  183. rowIndex++;
  184. rowData.push(query.value(0));
  185. output.writeCharacters(sanitize(query.value(0)));
  186. output.writeEndElement();
  187. output.writeEndElement();
  188. for(var i = 1; i < 9; i++) {
  189. output.writeTextElement("td", query.value(i));
  190. }
  191. output.writeEndElement();
  192. }
  193. query = query.invalidate();
  194. output.writeEndElement();
  195. output.writeEndElement();
  196. output.writeEndElement();
  197. output.writeEndElement();
  198. output.writeEndDocument();
  199. report.setContent(buffer);
  200. buffer.close();
  201. }
  202. refresh();
  203. sortBox['currentIndexChanged(int)'].connect(function() {
  204. QSettings.setValue("auco_sort", sortBox.currentIndex);
  205. refresh();
  206. });
  207. var notifier = Application.subscribe("transactionschange");
  208. notifier.notify.connect(function() {
  209. refresh();
  210. });
  211. report.scriptLinkClicked.connect(function(url) {
  212. if(url[0] == 'i') {
  213. url = url.slice(1, url.length);
  214. var itemReport = createReport("itemtransactions.xml");
  215. var sIB = findChildObject(itemReport, 'item');
  216. sIB.currentIndex = sIB.findData(url);
  217. return;
  218. }
  219. var element = new WebElement(report.findFirstElement("#" + url));
  220. var regular = url[0] == 'r';
  221. var index = url.slice(1, url.length);
  222. var tableref;
  223. if(regular) {
  224. tableref = "regular_coffees";
  225. } else {
  226. tableref = "decaf_coffees";
  227. }
  228. var origin = rowData[Number(url.slice(1, url.length))];
  229. var details = '<tr><td /><td colspan="6"><table><tr><th>' +
  230. TTR("useandcostreport", "Id") + '</th><th>' +
  231. TTR("useandcostreport", "Name") + '</th><th>' +
  232. TTR("useandcostreport", "Rate") + '</th><th>' +
  233. TTR("useandcostreport", "Cost") + '</th><th>' +
  234. TTR("useandcostreport", "Inventory") + '</th><th>' +
  235. TTR("useandcostreport", "First Use") + '</th><th>' +
  236. TTR("useandcostreport", "Last Use") + '</th></tr>';
  237. var query = new QSqlQuery();
  238. query.prepare("SELECT id, name, (rate/:conversion1)::numeric(12,3), (SELECT (cost*:conversion2)::numeric(12,2) FROM purchase WHERE item = id), (stock/:conversion3)::numeric(12,3), (SELECT min(time)::date FROM use WHERE item = id) AS first_use, (SELECT max(time)::date FROM use WHERE item = id) AS last_use FROM coffee_history WHERE origin = :origin AND id IN (SELECT id FROM " + tableref + ") AND id IN (SELECT item FROM transactions WHERE time >= :startDate AND time < :endDate::date + interval '1 day') ORDER BY first_use DESC");
  239. var conversion = 1;
  240. if(unitBox.currentIndex == 0) {
  241. conversion = 2.2;
  242. }
  243. var dateRange = dateSelect.currentRange();
  244. var startDate = dateRange[0];
  245. var endDate = dateRange[dateRange.length - 1];
  246. query.bind(":conversion1", conversion);
  247. query.bind(":conversion2", conversion);
  248. query.bind(":conversion3", conversion);
  249. query.bind(":origin", origin);
  250. query.bind(":startDate", startDate);
  251. query.bind(":endDate", endDate);
  252. query.exec();
  253. while(query.next()) {
  254. details += "<tr>";
  255. details += '<td><a href="typica://script/i' + query.value(0) + '">' + query.value(0) + "</a></td>";
  256. for(var i = 1; i < 7; i++) {
  257. details += "<td>" + sanitize(query.value(i)) + "</td>";
  258. }
  259. details += "</tr>";
  260. }
  261. query = query.invalidate();
  262. details += "</table></td></tr>";
  263. element.appendOutside(details);
  264. });
  265. function sanitize(value) {
  266. var replacement_chars = {
  267. "&": "&amp;",
  268. "<": "&lt;",
  269. ">": "&gt;",
  270. "\"": "&quot;",
  271. "'": "&#39;"
  272. };
  273. return value.replace(/[&<>"']/g, function(m) { return replacement_chars[m]; });
  274. }
  275. ]]>
  276. </program>
  277. </window>