Browse Source

Merge branch 'release-1.7'

Neal Wilson 8 years ago
parent
commit
0485b77027
100 changed files with 47626 additions and 37270 deletions
  1. 1
    1
      README
  2. 191
    148
      config/Reports/auco.xml
  3. 167
    116
      config/Reports/chart.xml
  4. 164
    143
      config/Reports/cogr.xml
  5. 83
    28
      config/Reports/dailyproductiondetail.xml
  6. 25
    13
      config/Reports/fypurchase.xml
  7. 28
    13
      config/Reports/greensales.xml
  8. 320
    0
      config/Reports/historyreport.xml
  9. 24
    25
      config/Reports/invchange.xml
  10. 30
    30
      config/Reports/inventory.xml
  11. 156
    0
      config/Reports/invoices.xml
  12. 42
    36
      config/Reports/itemtransactions.xml
  13. 117
    106
      config/Reports/monthcompare.xml
  14. 186
    0
      config/Reports/productionsummary.xml
  15. 186
    0
      config/Reports/reminders.xml
  16. 110
    61
      config/Reports/rwacp.xml
  17. 68
    0
      config/Scripts/reminders.css
  18. 23
    0
      config/Scripts/reminders.js
  19. BIN
      config/Translations/config.de.qm
  20. 2810
    0
      config/Translations/config.de.ts
  21. 2309
    0
      config/Translations/config.ts
  22. 0
    178
      config/Windows/batchdetails.xml
  23. 336
    325
      config/Windows/batchdetailsnew.xml
  24. 56
    40
      config/Windows/editbatchdetails.xml
  25. 6
    4
      config/Windows/editfee.xml
  26. 210
    0
      config/Windows/editreminder.xml
  27. 6
    6
      config/Windows/export.xml
  28. 0
    276
      config/Windows/externalroaster.xml
  29. 76
    89
      config/Windows/greeninventory.xml
  30. 3
    3
      config/Windows/greensales.xml
  31. 0
    47
      config/Windows/history.xml
  32. 1
    1
      config/Windows/importprofiles.xml
  33. 4
    1
      config/Windows/invoiceinfo.xml
  34. 0
    28
      config/Windows/invoicelist.xml
  35. 202
    198
      config/Windows/navigation.xml
  36. 484
    275
      config/Windows/newbatch.xml
  37. 42
    9
      config/Windows/newsamplebatch.xml
  38. 8
    8
      config/Windows/offline.xml
  39. 6
    6
      config/Windows/print.xml
  40. 723
    705
      config/Windows/productionroaster.xml
  41. 4
    4
      config/Windows/roastmanager.xml
  42. 92
    0
      config/Windows/roastspec.xml
  43. 1
    1
      config/Windows/setsampleparameters.xml
  44. 24
    24
      config/config.xml
  45. 0
    1
      docs/documentation.html
  46. 4
    8
      docs/documentation/part2.html
  47. 79
    0
      docs/documentation/windowreference/databaseconnection.html
  48. BIN
      docs/documentation/windowreference/databaseconnection.png
  49. BIN
      docs/documentation/windowreference/databasemenu.png
  50. BIN
      docs/documentation/windowreference/inventoryreports.png
  51. 202
    0
      docs/documentation/windowreference/logging.html
  52. BIN
      docs/documentation/windowreference/logging.png
  53. BIN
      docs/documentation/windowreference/loggingbatch.png
  54. BIN
      docs/documentation/windowreference/loggingfile.png
  55. BIN
      docs/documentation/windowreference/logginggraph.png
  56. BIN
      docs/documentation/windowreference/logginglog.png
  57. 156
    0
      docs/documentation/windowreference/navigation.html
  58. BIN
      docs/documentation/windowreference/navigation.png
  59. 38
    0
      docs/documentation/windowreference/openconfigurationfile.html
  60. BIN
      docs/documentation/windowreference/openconfigurationfile.png
  61. BIN
      docs/documentation/windowreference/productionreports.png
  62. BIN
      docs/documentation/windowreference/purchasereports.png
  63. BIN
      docs/documentation/windowreference/reportsmenu.png
  64. BIN
      docs/documentation/windowreference/salesreport.png
  65. 1396
    0
      src/Translations/Typica.ts
  66. BIN
      src/Translations/Typica_de.qm
  67. 1396
    0
      src/Translations/Typica_de.ts
  68. 3
    0
      src/Typica.pro
  69. 5
    5
      src/abouttypica.cpp
  70. 2
    2
      src/abouttypica.h
  71. 20
    20
      src/daterangeselector.cpp
  72. 4
    4
      src/daterangeselector.h
  73. 1
    1
      src/daterangeselector.w
  74. 2
    2
      src/draglabel.cpp
  75. 2
    2
      src/draglabel.h
  76. 7
    7
      src/helpmenu.cpp
  77. 2
    2
      src/helpmenu.h
  78. 69
    0
      src/licensewindow.cpp
  79. 24
    0
      src/licensewindow.h
  80. 6
    3
      src/moc_helpmenu.cpp
  81. 15
    6
      src/moc_typica.cpp
  82. 31810
    31572
      src/qrc_resources.cpp
  83. 6
    6
      src/resources/Info.plist
  84. 7
    83
      src/resources/html/about.html
  85. 6
    6
      src/scale.cpp
  86. 2
    2
      src/scale.h
  87. 2697
    2453
      src/typica.cpp
  88. 6
    6
      src/typica.rc
  89. 274
    25
      src/typica.w
  90. 6
    6
      src/units.cpp
  91. 2
    2
      src/units.h
  92. 5
    5
      src/webelement.cpp
  93. 2
    2
      src/webelement.h
  94. 11
    11
      src/webview.cpp
  95. 2
    2
      src/webview.h
  96. 11
    31
      web/output/download-mac-latest.html
  97. 11
    31
      web/output/download-windows-latest.html
  98. 3
    3
      web/output/downloads.html
  99. 8
    13
      web/output/index.html
  100. 0
    0
      web/src/pages/download-mac-latest.m4

+ 1
- 1
README View File

6
 
6
 
7
 Typica is free software released under the MIT license as follows:
7
 Typica is free software released under the MIT license as follows:
8
 
8
 
9
-Copyright 2007-2015 Neal Evan Wilson
9
+Copyright 2007-2016 Neal Evan Wilson
10
 
10
 
11
 Permission is hereby granteed, free of charge, to any person obtaining a copy
11
 Permission is hereby granteed, free of charge, to any person obtaining a copy
12
 of this software and associated documentation files (the "Software"), to deal
12
 of this software and associated documentation files (the "Software"), to deal

+ 191
- 148
config/Reports/auco.xml View File

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

+ 167
- 116
config/Reports/chart.xml View File

2
 	<reporttitle>Production:->Previous Year Production Comparison</reporttitle>
2
 	<reporttitle>Production:->Previous Year Production Comparison</reporttitle>
3
     <layout type="vertical">
3
     <layout type="vertical">
4
         <layout type="horizontal">
4
         <layout type="horizontal">
5
-			<daterange id="dates" initial="19" /><!-- Current Year to Date -->
6
-            <label>Days to Average</label>
5
+            <label>Batch Type:</label>
6
+            <sqldrop id="batchtype" />
7
+            <label>Approval:</label>
8
+            <sqldrop id="approval" />
9
+            <daterange id="dates" initial="19" /><!-- Current Year to Date -->
10
+            <label>Days to Average:</label>
7
             <line validator="integer" id="days">7</line>
11
             <line validator="integer" id="days">7</line>
8
-			<label>Weight Unit:</label>
9
-			<sqldrop id="unit" />
12
+            <label>Weight Unit:</label>
13
+            <sqldrop id="unit" />
10
             <stretch />
14
             <stretch />
11
         </layout>
15
         </layout>
12
         <webview id="report" />
16
         <webview id="report" />
13
     </layout>
17
     </layout>
14
     <menu name="File">
18
     <menu name="File">
15
-        <item id="print" shortcut="Ctrl+P">Print</item>
19
+        <item id="print" shortcut="Ctrl+P">Print...</item>
16
     </menu>
20
     </menu>
17
     <program>
21
     <program>
18
         <![CDATA[
22
         <![CDATA[
19
-            this.windowTitle = "Typica - Previous Year Production Comparison";
20
-			var dateSelect = findChildObject(this, 'dates');
21
-			dateSelect.removeIndex(23); // Remove Lifetime range
23
+            this.windowTitle = TTR("pytdprodcomp", "Typica - Previous Year Production Comparison");
24
+            var dateSelect = findChildObject(this, 'dates');
25
+            dateSelect.removeIndex(23); // Remove Lifetime range
22
             var view = findChildObject(this, 'report');
26
             var view = findChildObject(this, 'report');
23
             var printMenu = findChildObject(this, 'print');
27
             var printMenu = findChildObject(this, 'print');
24
             printMenu.triggered.connect(function() {
28
             printMenu.triggered.connect(function() {
25
                 view.print();
29
                 view.print();
26
             });
30
             });
27
             var avgField = findChildObject(this, 'days');
31
             var avgField = findChildObject(this, 'days');
28
-			var unitBox = findChildObject(this, 'unit');
29
-			unitBox.addItem("Kg");
30
-			unitBox.addItem("Lb");
31
-			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
32
-			unitBox['currentIndexChanged(int)'].connect(function() {
33
-				QSettings.setValue("script/report_unit", unitBox.currentIndex);
34
-				refresh();
35
-			});
32
+            var unitBox = findChildObject(this, 'unit');
33
+            unitBox.addItem(TTR("pytdprodcomp", "Kg"));
34
+            unitBox.addItem(TTR("pytdprodcomp", "Lb"));
35
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
36
+            unitBox['currentIndexChanged(int)'].connect(function() {
37
+                QSettings.setValue("script/report_unit", unitBox.currentIndex);
38
+                refresh();
39
+            });
40
+            var batchType = findChildObject(this, 'batchtype');
41
+            batchType.addItem(TTR("pytdprodcomp", "Any"));
42
+            batchType.addItem(TTR("pytdprodcomp", "Production Roasts"));
43
+            batchType.addItem(TTR("pytdprodcomp", "Sample Roasts"));
44
+            batchType.currentIndex = QSettings.value("script/batchtypefilter", 1);
45
+            batchType['currentIndexChanged(int)'].connect(function() {
46
+                QSettings.setValue("script/batchtypefilter", batchType.currentIndex);
47
+                refresh();
48
+            });
49
+            var approval = findChildObject(this, 'approval');
50
+            approval.addItem(TTR("pytdprodcomp", "Any"));
51
+            approval.addItem(TTR("pytdprodcomp", "Approved"));
52
+            approval.addItem(TTR("pytdprodcomp", "Not Approved"));
53
+            approval.currentIndex = QSettings.value("script/approvalfilter", 1);
54
+            approval['currentIndexChanged(int)'].connect(function() {
55
+                QSettings.setValue("script/approvalfilter", approval.currentIndex);
56
+                refresh();
57
+            });
36
             function refresh() {
58
             function refresh() {
37
-				var conversion = 1;
38
-				if(unitBox.currentIndex == 0) {
39
-					conversion = 2.2;
40
-				}
59
+                var conversion = 1;
60
+                if(unitBox.currentIndex == 0) {
61
+                    conversion = 2.2;
62
+                }
41
                 var buffer = new QBuffer;
63
                 var buffer = new QBuffer;
42
                 buffer.open(3);
64
                 buffer.open(3);
43
                 var output = new XmlWriter(buffer);
65
                 var output = new XmlWriter(buffer);
46
                 output.writeStartElement("html");
68
                 output.writeStartElement("html");
47
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
69
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
48
                 output.writeStartElement("head");
70
                 output.writeStartElement("head");
49
-                output.writeTextElement("title", "Previous Year Production Comparison");
71
+                output.writeTextElement("title", TTR("pytdprodcomp", "Previous Year Production Comparison"));
50
                 output.writeEndElement();
72
                 output.writeEndElement();
51
                 output.writeStartElement("body");
73
                 output.writeStartElement("body");
52
-                output.writeTextElement("h1", "Previous Year Production Comparison");
53
-                output.writeTextElement("p", "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.");
74
+                output.writeTextElement("h1", TTR("pytdprodcomp", "Previous Year Production Comparison"));
75
+                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."));
54
                 output.writeStartElement("table");
76
                 output.writeStartElement("table");
55
                 output.writeAttribute("style", "page-break-after:auto;");
77
                 output.writeAttribute("style", "page-break-after:auto;");
56
                 output.writeAttribute("rules", "groups");
78
                 output.writeAttribute("rules", "groups");
58
                 output.writeStartElement("thead");
80
                 output.writeStartElement("thead");
59
                 output.writeStartElement("tr");
81
                 output.writeStartElement("tr");
60
                 output.writeTextElement("th", "Coffee");
82
                 output.writeTextElement("th", "Coffee");
61
-				switch(unitBox.currentIndex) {
62
-					case 0:
63
-						output.writeTextElement("th", "Previous (Kg)");
64
-						output.writeTextElement("th", "Current (Kg)");
65
-						output.writeTextElement("th", "Change (Kg)");
66
-						break;
67
-					case 1:
68
-						output.writeTextElement("th", "Previous (Lb)");
69
-						output.writeTextElement("th", "Current (Lb)");
70
-						output.writeTextElement("th", "Change (Lb)");
71
-						break;
72
-				}
83
+                switch(unitBox.currentIndex) {
84
+                    case 0:
85
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Kg)"));
86
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Current (Kg)"));
87
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Change (Kg)"));
88
+                        break;
89
+                    case 1:
90
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Lb)"));
91
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Current (Lb)"));
92
+                        output.writeTextElement("th", TTR("pytdprodcomp", "Change (Lb)"));
93
+                        break;
94
+                }
73
                 output.writeEndElement();
95
                 output.writeEndElement();
74
                 output.writeEndElement();
96
                 output.writeEndElement();
75
                 output.writeStartElement("tbody");
97
                 output.writeStartElement("tbody");
76
                 var query = new QSqlQuery();
98
                 var query = new QSqlQuery();
77
                 query.exec("START TRANSACTION");
99
                 query.exec("START TRANSACTION");
78
-				var dateRange = dateSelect.currentRange();
79
-				var curStartDate = "'"+dateRange[0]+"'";
80
-				var curEndDate = "'"+dateRange[dateRange.length - 1]+"'";
100
+                var dateRange = dateSelect.currentRange();
101
+                var curStartDate = "'"+dateRange[0]+"'";
102
+                var curEndDate = "'"+dateRange[dateRange.length - 1]+"'";
81
                 query.exec("SELECT "+curStartDate+"::date - interval '1 year', "+curEndDate+"::date - interval '1 year' + interval '1 day', "+curEndDate+"::date + interval '1 day'");
103
                 query.exec("SELECT "+curStartDate+"::date - interval '1 year', "+curEndDate+"::date - interval '1 year' + interval '1 day', "+curEndDate+"::date + interval '1 day'");
82
-				query.next();
104
+                query.next();
83
                 curEndDate = "'"+query.value(2)+"'";
105
                 curEndDate = "'"+query.value(2)+"'";
84
                 var prevStartDate = "'"+query.value(0)+"'";
106
                 var prevStartDate = "'"+query.value(0)+"'";
85
                 var prevEndDate = "'"+query.value(1)+"'";
107
                 var prevEndDate = "'"+query.value(1)+"'";
86
-                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 GROUP BY roasted_id";
108
+                var transaction_filter;
109
+                var approval_filter;
110
+                switch(batchType.currentIndex) {
111
+                    case 0:
112
+                        transaction_filter = "";
113
+                        break;
114
+                    case 1:
115
+                        transaction_filter = " AND transaction_type = 'ROAST'";
116
+                        break;
117
+                    case 2:
118
+                        transaction_filter = " AND transaction_type = 'SAMPLEROAST'";
119
+                        break;
120
+                }
121
+                switch(approval.currentIndex) {
122
+                    case 0:
123
+                        approval_filter = "";
124
+                        break;
125
+                    case 1:
126
+                        approval_filter = " AND approval = true";
127
+                        break;
128
+                    case 2:
129
+                        approval_filter = " AND approval = false";
130
+                        break;
131
+                }
132
+                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";
87
                 query.exec(q);
133
                 query.exec(q);
88
-                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 GROUP BY roasted_id";
134
+                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";
89
                 query.exec(q);
135
                 query.exec(q);
90
                 query.exec("INSERT INTO previous SELECT roasted_id, 0 FROM current WHERE roasted_id NOT IN (SELECT roasted_id FROM previous)");
136
                 query.exec("INSERT INTO previous SELECT roasted_id, 0 FROM current WHERE roasted_id NOT IN (SELECT roasted_id FROM previous)");
91
                 query.exec("INSERT INTO current SELECT roasted_id, 0 FROM previous WHERE roasted_id NOT IN (SELECT roasted_id FROM current)");
137
                 query.exec("INSERT INTO current SELECT roasted_id, 0 FROM previous WHERE roasted_id NOT IN (SELECT roasted_id FROM current)");
95
                 {
141
                 {
96
                     output.writeStartElement("tr");
142
                     output.writeStartElement("tr");
97
                     output.writeTextElement("td", query.value(0));
143
                     output.writeTextElement("td", query.value(0));
98
-					output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
99
-					output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
100
-					output.writeTextElement("td", (query.value(3) / conversion).toFixed(2));
144
+                    output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
145
+                    output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
146
+                    output.writeTextElement("td", (query.value(3) / conversion).toFixed(2));
101
                     output.writeEndElement();
147
                     output.writeEndElement();
102
                 }
148
                 }
103
                 output.writeEndElement();
149
                 output.writeEndElement();
104
                 output.writeStartElement("tfoot");
150
                 output.writeStartElement("tfoot");
105
-                output.writeTextElement("th", "Totals");
151
+                output.writeTextElement("th", TTR("pytdprodcomp", "Totals"));
106
                 query.exec("SELECT sum(p), sum(c), sum(c-p) FROM comp");
152
                 query.exec("SELECT sum(p), sum(c), sum(c-p) FROM comp");
107
                 query.next();
153
                 query.next();
108
-				output.writeTextElement("td", (query.value(0) / conversion).toFixed(2));
109
-				output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
110
-				output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
154
+                output.writeTextElement("td", (query.value(0) / conversion).toFixed(2));
155
+                output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
156
+                output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
111
                 output.writeEndElement();
157
                 output.writeEndElement();
112
                 query.exec("ABORT");
158
                 query.exec("ABORT");
113
                 output.writeEndElement();
159
                 output.writeEndElement();
114
-				output.writeStartElement("p");
115
-				output.writeAttribute("style", "page-break-inside: avoid");
160
+                output.writeStartElement("p");
161
+                output.writeAttribute("style", "page-break-inside: avoid");
116
                 output.writeStartElement("svg");
162
                 output.writeStartElement("svg");
117
                 output.writeAttribute("xmlns", "http://www.w3.org/2000/svg");
163
                 output.writeAttribute("xmlns", "http://www.w3.org/2000/svg");
118
                 output.writeAttribute("width", "7.5in");
164
                 output.writeAttribute("width", "7.5in");
158
                     query.exec(q);
204
                     query.exec(q);
159
                     query.next();
205
                     query.next();
160
                     dates[i] = query.value(0);
206
                     dates[i] = query.value(0);
161
-                    q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+" AND time < '"+dates[i]+"'";
207
+                    q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+" AND time < '"+dates[i]+"'" + transaction_filter + approval_filter;
162
                     query.exec(q);
208
                     query.exec(q);
163
                     if(query.next())
209
                     if(query.next())
164
                     {
210
                     {
165
-                        curpounds[i] = query.value(0);
211
+                        curpounds[i] = Number(query.value(0));
166
                     }
212
                     }
167
                     else
213
                     else
168
                     {
214
                     {
169
                         curpounds[i] = 0;
215
                         curpounds[i] = 0;
170
                     }
216
                     }
171
-                    q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+"::date - '1 year'::interval AND time < '"+dates[i]+"'::date - '1 year'::interval";
217
+                    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;
172
                     query.exec(q);
218
                     query.exec(q);
173
                     if(query.next())
219
                     if(query.next())
174
                     {
220
                     {
175
-                        prevpounds[i] = query.value(0);
221
+                        prevpounds[i] = Number(query.value(0));
176
                     }
222
                     }
177
                     else
223
                     else
178
                     {
224
                     {
204
                         avgchange[i] = sum / parseInt(avgField.text);
250
                         avgchange[i] = sum / parseInt(avgField.text);
205
                     }
251
                     }
206
                 }
252
                 }
207
-				// Calculate the domain of the primary y axis.
253
+                // Calculate the domain of the primary y axis.
208
                 var maxy1 = 0;
254
                 var maxy1 = 0;
209
-				var increment = 100; // Starting increment is 100 Lb.
210
-				if(unitBox.currentIndex == 0) {
211
-					increment = 110; // Starting increment is 50 Kg if Kg is requested.
212
-				}
255
+                    var increment = 100; // Starting increment is 100 Lb.
256
+                    if(unitBox.currentIndex == 0) {
257
+                        increment = 110; // Starting increment is 50 Kg if Kg is requested.
258
+                    }
213
                 while(maxy1 < Math.max(curpounds[days - 1], prevpounds[days - 1]))
259
                 while(maxy1 < Math.max(curpounds[days - 1], prevpounds[days - 1]))
214
                 {
260
                 {
215
-					maxy1 += increment;
261
+                    maxy1 += increment;
262
+                }
263
+                // Calculate the number of grid lines and loosen spacing if there are too many.
264
+                var divisions = maxy1 / increment;
265
+                while(divisions > 10) {
266
+                    increment *= 2;
267
+                    divisions = maxy1 / increment;
216
                 }
268
                 }
217
-				// Calculate the number of grid lines and loosen spacing if there are too many.
218
-				var divisions = maxy1 / increment;
219
-				while(divisions > 10) {
220
-					increment *= 2;
221
-					divisions = maxy1 / increment;
222
-				}
223
-				// Draw y axis grid lines.
269
+                // Draw y axis grid lines.
224
                 var pos = 0;
270
                 var pos = 0;
225
                 while(pos <= maxy1)
271
                 while(pos <= maxy1)
226
                 {
272
                 {
231
                     output.writeAttribute("y2", (450/maxy1)*pos);
277
                     output.writeAttribute("y2", (450/maxy1)*pos);
232
                     output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
278
                     output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
233
                     output.writeEndElement();
279
                     output.writeEndElement();
234
-					pos += increment;
280
+                    pos += increment;
235
                 }
281
                 }
236
                 var n = Math.min.apply(Math, change);
282
                 var n = Math.min.apply(Math, change);
237
                 var m = Math.max.apply(Math, change);
283
                 var m = Math.max.apply(Math, change);
334
                     output.writeAttribute("x", "0");
380
                     output.writeAttribute("x", "0");
335
                     output.writeAttribute("y", -((450/maxy1)*i)+5);
381
                     output.writeAttribute("y", -((450/maxy1)*i)+5);
336
                     output.writeAttribute("font-size", "12");
382
                     output.writeAttribute("font-size", "12");
337
-					switch(unitBox.currentIndex) {
338
-						case 0:
339
-							output.writeCharacters((i / 2.2).toFixed(0));
340
-							break;
341
-						case 1:
342
-						default:
343
-							output.writeCharacters(i);
344
-							break;
345
-					}
383
+                    switch(unitBox.currentIndex) {
384
+                        case 0:
385
+                            output.writeCharacters((i / 2.2).toFixed(0));
386
+                            break;
387
+                        case 1:
388
+                        default:
389
+                            output.writeCharacters(i);
390
+                            break;
391
+                    }
346
                     output.writeEndElement();
392
                     output.writeEndElement();
347
                     i += increment;
393
                     i += increment;
348
                 }
394
                 }
349
                 i = miny2;
395
                 i = miny2;
396
+                var stepSize = 0.1;
397
+                while((maxy2 - miny2) / stepSize > 20)
398
+                {
399
+                    stepSize += 0.05;
400
+                }
350
                 while(i <= maxy2)
401
                 while(i <= maxy2)
351
                 {
402
                 {
352
                     output.writeStartElement("text");
403
                     output.writeStartElement("text");
355
                     output.writeAttribute("font-size", "12");
406
                     output.writeAttribute("font-size", "12");
356
                     output.writeCharacters(Number(i*100).toFixed(0)+"%");
407
                     output.writeCharacters(Number(i*100).toFixed(0)+"%");
357
                     output.writeEndElement();
408
                     output.writeEndElement();
358
-                    i += 0.1;
409
+                    i += stepSize;
359
                 }
410
                 }
360
                 i = 0;
411
                 i = 0;
361
                 while(i <= days-1)
412
                 while(i <= days-1)
373
                     switch(parts[1])
424
                     switch(parts[1])
374
                     {
425
                     {
375
                         case '01':
426
                         case '01':
376
-                            ds = "January ";
427
+                            ds = TTR("pytdprodcomp", "January ");
377
                             break;
428
                             break;
378
                         case '02':
429
                         case '02':
379
-                            ds = "February ";
430
+                            ds = TTR("pytdprodcomp", "February ");
380
                             break;
431
                             break;
381
                         case '03':
432
                         case '03':
382
-                            ds = "March ";
433
+                            ds = TTR("pytdprodcomp", "March ");
383
                             break;
434
                             break;
384
                         case '04':
435
                         case '04':
385
-                            ds = "April ";
436
+                            ds = TTR("pytdprodcomp", "April ");
386
                             break;
437
                             break;
387
                         case '05':
438
                         case '05':
388
-                            ds = "May ";
439
+                            ds = TTR("pytdprodcomp", "May ");
389
                             break;
440
                             break;
390
                         case '06':
441
                         case '06':
391
-                            ds = "June ";
442
+                            ds = TTR("pytdprodcomp", "June ");
392
                             break;
443
                             break;
393
                         case '07':
444
                         case '07':
394
-                            ds = "July ";
445
+                            ds = TTR("pytdprodcomp", "July ");
395
                             break;
446
                             break;
396
                         case '08':
447
                         case '08':
397
-                            ds = "August ";
448
+                            ds = TTR("pytdprodcomp", "August ");
398
                             break;
449
                             break;
399
                         case '09':
450
                         case '09':
400
-                            ds = "September ";
451
+                            ds = TTR("pytdprodcomp", "September ");
401
                             break;
452
                             break;
402
                         case '10':
453
                         case '10':
403
-                            ds = "October ";
454
+                            ds = TTR("pytdprodcomp", "October ");
404
                             break;
455
                             break;
405
                         case '11':
456
                         case '11':
406
-                            ds = "November ";
457
+                            ds = TTR("pytdprodcomp", "November ");
407
                             break;
458
                             break;
408
                         case '12':
459
                         case '12':
409
-                            ds = "December ";
460
+                            ds = TTR("pytdprodcomp", "December ");
410
                             break;
461
                             break;
411
                     }
462
                     }
412
                     ds = ds + Number(parts[2]);
463
                     ds = ds + Number(parts[2]);
422
                         i = days - 1;
473
                         i = days - 1;
423
                     }
474
                     }
424
                 }
475
                 }
425
-				query = query.invalidate();
476
+                query = query.invalidate();
426
                 output.writeStartElement("rect");
477
                 output.writeStartElement("rect");
427
                 output.writeAttribute("fill", "rgb(0,0,0)");
478
                 output.writeAttribute("fill", "rgb(0,0,0)");
428
                 output.writeAttribute("x", "45");
479
                 output.writeAttribute("x", "45");
434
                 output.writeAttribute("x", "75");
485
                 output.writeAttribute("x", "75");
435
                 output.writeAttribute("y", "120");
486
                 output.writeAttribute("y", "120");
436
                 output.writeAttribute("font-size", "12");
487
                 output.writeAttribute("font-size", "12");
437
-				switch(unitBox.currentIndex) {
438
-					case 0:
439
-						output.writeCharacters("Previous Year Kg");
440
-						break;
441
-					case 1:
442
-						output.writeCharacters("Previous Year Lb");
443
-						break;
444
-				}
488
+                switch(unitBox.currentIndex) {
489
+                    case 0:
490
+                        output.writeCharacters(TTR("pytdprodcomp", "Previous Year Kg"));
491
+                        break;
492
+                    case 1:
493
+                        output.writeCharacters(TTR("pytdprodcomp", "Previous Year Lb"));
494
+                        break;
495
+                }
445
                 output.writeEndElement();
496
                 output.writeEndElement();
446
                 output.writeStartElement("rect");
497
                 output.writeStartElement("rect");
447
                 output.writeAttribute("fill", "rgb(255,0,0)");
498
                 output.writeAttribute("fill", "rgb(255,0,0)");
454
                 output.writeAttribute("x", "225");
505
                 output.writeAttribute("x", "225");
455
                 output.writeAttribute("y", "120");
506
                 output.writeAttribute("y", "120");
456
                 output.writeAttribute("font-size", "12");
507
                 output.writeAttribute("font-size", "12");
457
-				switch(unitBox.currentIndex) {
458
-					case 0:
459
-						output.writeCharacters("Current Year Kg");
460
-						break;
461
-					case 1:
462
-						output.writeCharacters("Current Year Lb");
463
-						break;
464
-				}
508
+                switch(unitBox.currentIndex) {
509
+                    case 0:
510
+                        output.writeCharacters(TTR("pytdprodcomp", "Current Year Kg"));
511
+                        break;
512
+                    case 1:
513
+                        output.writeCharacters(TTR("pytdprodcomp", "Current Year Lb"));
514
+                        break;
515
+                }
465
                 output.writeEndElement();
516
                 output.writeEndElement();
466
                 output.writeStartElement("rect");
517
                 output.writeStartElement("rect");
467
                 output.writeAttribute("fill", "rgb(0,255,0)");
518
                 output.writeAttribute("fill", "rgb(0,255,0)");
474
                 output.writeAttribute("x", "375");
525
                 output.writeAttribute("x", "375");
475
                 output.writeAttribute("y", "120");
526
                 output.writeAttribute("y", "120");
476
                 output.writeAttribute("font-size", "12");
527
                 output.writeAttribute("font-size", "12");
477
-                output.writeCharacters("% Change");
528
+                output.writeCharacters(TTR("pytdprodcomp", "% Change"));
478
                 output.writeEndElement();
529
                 output.writeEndElement();
479
                 output.writeStartElement("rect");
530
                 output.writeStartElement("rect");
480
                 output.writeAttribute("fill", "rgb(0,0,255)");
531
                 output.writeAttribute("fill", "rgb(0,0,255)");
487
                 output.writeAttribute("x", "525");
538
                 output.writeAttribute("x", "525");
488
                 output.writeAttribute("y", "120");
539
                 output.writeAttribute("y", "120");
489
                 output.writeAttribute("font-size", "12");
540
                 output.writeAttribute("font-size", "12");
490
-                output.writeCharacters("Average % Change");
541
+                output.writeCharacters(TTR("pytdprodcomp", "Average % Change"));
542
+                output.writeEndElement();
491
                 output.writeEndElement();
543
                 output.writeEndElement();
492
                 output.writeEndElement();
544
                 output.writeEndElement();
493
                 output.writeEndElement();
545
                 output.writeEndElement();
494
                 output.writeEndElement();
546
                 output.writeEndElement();
495
                 output.writeEndElement();
547
                 output.writeEndElement();
496
-				output.writeEndElement();
497
                 output.writeEndDocument();
548
                 output.writeEndDocument();
498
                 view.setContent(buffer);
549
                 view.setContent(buffer);
499
                 buffer.close();
550
                 buffer.close();
500
             }
551
             }
501
             refresh();
552
             refresh();
502
-			dateSelect.rangeUpdated.connect(function() {
503
-				refresh();
504
-			});
505
-			avgField.editingFinished.connect(function() {
506
-				refresh();
507
-			});
553
+            dateSelect.rangeUpdated.connect(function() {
554
+                refresh();
555
+            });
556
+            avgField.editingFinished.connect(function() {
557
+                refresh();
558
+            });
508
         ]]>
559
         ]]>
509
     </program>
560
     </program>
510
 </window>
561
 </window>

+ 164
- 143
config/Reports/cogr.xml View File

1
 <window id="greencost">
1
 <window id="greencost">
2
-	<reporttitle>Production:->Cost of Green Coffee for Roasted Coffee</reporttitle>
3
-	<layout type="vertical">
4
-		<webview id="report" />
5
-	</layout>
6
-	<menu name="File">
7
-		<item id="print" shortcut="Ctrl+P">Print</item>
8
-	</menu>
9
-	<program>
10
-		<![CDATA[
11
-			this.windowTitle = "Typica - Cost of Green Coffee for Roasted Coffee";
12
-			var report = findChildObject(this, 'report');
13
-			var printMenu = findChildObject(this, 'print');
14
-			printMenu.triggered.connect(function() {
15
-				report.print();
16
-			});
17
-			function refresh() {
18
-				var buffer = new QBuffer;
19
-				buffer.open(3);
20
-				var output = new XmlWriter(buffer);
21
-				output.writeStartDocument("1.0");
22
-				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">');
2
+    <reporttitle>Production:->Cost of Green Coffee for Roasted Coffee</reporttitle>	<layout type="vertical">
3
+        <layout type="horizontal">
4
+            <label>Weight Unit:</label>
5
+            <sqldrop id="unit" />
6
+            <stretch />
7
+        </layout>
8
+        <webview id="report" />
9
+    </layout>
10
+    <menu name="File">
11
+        <item id="print" shortcut="Ctrl+P">Print...</item>
12
+    </menu>
13
+    <program>
14
+        <![CDATA[
15
+            this.windowTitle = TTR("greencost", "Typica - Cost of Green Coffee for Roasted Coffee");
16
+            var report = findChildObject(this, 'report');
17
+            var printMenu = findChildObject(this, 'print');
18
+            printMenu.triggered.connect(function() {
19
+                report.print();
20
+            });
21
+            var unitBox = findChildObject(this, 'unit');
22
+            unitBox.addItem(TTR("greencost", "Kg"));
23
+            unitBox.addItem(TTR("greencost", "Lb"));
24
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
25
+            unitBox['currentIndexChanged(int)'].connect(function() {
26
+                QSettings.setValue("script/report_unit", unitBox.currentIndex);
27
+                refresh();
28
+            });
29
+            function refresh() {
30
+                var conversion = 1;
31
+                if(unitBox.currentIndex == 0) {
32
+                    conversion = 2.2;
33
+                }
34
+                var buffer = new QBuffer;
35
+                buffer.open(3);
36
+                var output = new XmlWriter(buffer);
37
+                output.writeStartDocument("1.0");
38
+                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">');
23
                 output.writeStartElement("html");
39
                 output.writeStartElement("html");
24
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
40
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
25
                 output.writeStartElement("head");
41
                 output.writeStartElement("head");
26
-                output.writeTextElement("title", "Cost of Green Coffee for Roasted Coffee");
42
+                output.writeTextElement("title", TTR("greencost", "Cost of Green Coffee for Roasted Coffee"));
27
                 output.writeEndElement();
43
                 output.writeEndElement();
28
                 output.writeStartElement("body");
44
                 output.writeStartElement("body");
29
-                output.writeTextElement("h1", "Cost of Green Coffee for Roasted Coffee");
30
-				var query = new QSqlQuery();
31
-				query.exec("SELECT item, (SELECT name FROM items WHERE id = item) AS name FROM current_items ORDER BY name");
32
-				var roastedItems = new Array();
33
-				var roastedNames = new Array();
34
-				while(query.next()) {
35
-					roastedItems.push(query.value(0));
36
-					roastedNames.push(query.value(1));
37
-				}
38
-				var recipes = new Array();
39
-				var recipeQuantities = new Array();
40
-				query.prepare("SELECT unroasted_id, unroasted_quantity FROM roasting_log WHERE roasted_id = :item AND time = (SELECT max(time) FROM roasting_log WHERE roasted_id = :item2)");
41
-				for(var i = 0; i < roastedItems.length; i++) {
42
-					query.bind("item", roastedItems[i]);
43
-					query.bind("item2", roastedItems[i]);
44
-					query.exec();
45
-					if(query.next()) {
46
-						recipes.push(query.value(0));
47
-						recipeQuantities.push(query.value(1));
48
-					} else {
49
-						recipes.push("{-1}");
50
-						recipeQuantities.push("{-1}");
51
-					}
52
-				}
53
-				query.prepare("SELECT min(unroasted_total_quantity / roasted_quantity), max(unroasted_total_quantity / roasted_quantity), avg(unroasted_total_quantity / roasted_quantity) FROM roasting_log WHERE roasted_id = :ri AND unroasted_id = :gi AND unroasted_total_quantity > 0 AND roasted_quantity > 0 AND approval = true");
54
-				var mins = new Array();
55
-				var maxes = new Array();
56
-				var means = new Array();
57
-				for(var i = 0; i < roastedItems.length; i++) {
58
-					if(recipes[i] == "{-1}") {
59
-						mins.push("undefined");
60
-						maxes.push("undefined");
61
-						means.push("undefined");
62
-					} else {
63
-						query.bind(":ri", Number(roastedItems[i]));
64
-						query.bind(":gi", recipes[i]);
65
-						query.exec();
66
-						if(query.next()) {
67
-							mins.push(query.value(0));
68
-							maxes.push(query.value(1));
69
-							means.push(query.value(2));
70
-						} else {
71
-							print("Error 2");
72
-						}
73
-					}
74
-				}
75
-				var proportionalCosts = new Array();
76
-				query.prepare("SELECT cost * :proportion FROM purchase WHERE item = :id");
77
-				for(var i = 0; i < roastedItems.length; i++) {
78
-					if(recipes[i] == "{-1}") {
79
-						proportionalCosts.push("undefined");
80
-					} else {
81
-						greens = sqlToArray(recipes[i]);
82
-						weights = sqlToArray(recipeQuantities[i]);
83
-						proportions = new Array();
84
-						quantitySum = weights.reduce(function(p, c) {
85
-							return p + c;
86
-						});
87
-						for(var j = 0; j < weights.length; j++) {
88
-							proportions.push(weights[j]/quantitySum);
89
-						}
90
-						partialSum = 0;
91
-						for(var j = 0; j < greens.length; j++) {
92
-							query.bind(":proportion", proportions[j]);
93
-							query.bind(":id", greens[j]);
94
-							query.exec();
95
-							if(query.next()) {
96
-								partialSum += query.value(0);
97
-							} else {
98
-								print("Error 3");
99
-							}
100
-						}
101
-						proportionalCosts.push(partialSum);
102
-					}
103
-				}
104
-				query = query.invalidate();
105
-				var minCosts = new Array();
106
-				var maxCosts = new Array();
107
-				var meanCosts = new Array();
108
-				for(var i = 0; i < roastedItems.length; i++) {
109
-					if(recipes[i] == "{-1}") {
110
-						minCosts.push("undefined");
111
-						maxCosts.push("undefined");
112
-						meanCosts.push("undefined");
113
-					} else {
114
-						minCosts.push(proportionalCosts[i] * mins[i]);
115
-						maxCosts.push(proportionalCosts[i] * maxes[i]);
116
-						meanCosts.push(proportionalCosts[i] * means[i]);
117
-					}
118
-				}
119
-				output.writeStartElement("table");
120
-				output.writeAttribute("rules", "groups");
121
-				output.writeAttribute("cellpadding", "3px");
122
-				output.writeStartElement("thead");
123
-				output.writeStartElement("tr");
124
-				output.writeTextElement("th", "Coffee");
125
-				output.writeTextElement("th", "Minimum Cost");
126
-				output.writeTextElement("th", "Maximum Cost");
127
-				output.writeTextElement("th", "Mean Cost");
128
-				output.writeEndElement();
129
-				output.writeEndElement();
130
-				output.writeStartElement("tbody");
131
-				for(var i = 0; i < roastedItems.length; i++) {
132
-					output.writeStartElement("tr");
133
-					output.writeTextElement("td", roastedNames[i]);
134
-					output.writeTextElement("td", Number(minCosts[i]).toFixed(2));
135
-					output.writeTextElement("td", Number(maxCosts[i]).toFixed(2));
136
-					output.writeTextElement("td", Number(meanCosts[i]).toFixed(2));
137
-					output.writeEndElement();
138
-				}
139
-				output.writeEndElement();
140
-				output.writeEndElement();
141
-				output.writeEndElement();
142
-				output.writeEndElement();
143
-				output.writeEndDocument();
144
-				report.setContent(buffer);
145
-				buffer.close();
146
-			};
147
-			refresh();
148
-		]]>
149
-	</program>
45
+                var unit = (unitBox.currentIndex == 0 ? TTR("greencost", "Kg") : 
46
+                    TTR("greencost", "Lb"));
47
+                output.writeTextElement("h1", TTR("greencost", "Cost of Green Coffee for Roasted Coffee"));
48
+                output.writeTextElement("p", TTR("greencost", "Cost of green coffee per ") +
49
+                    unit + TTR("greencost", " of roasted coffee"));
50
+                var query = new QSqlQuery();
51
+                query.exec("SELECT item, (SELECT name FROM items WHERE id = item) AS name FROM current_items ORDER BY name");
52
+                var roastedItems = new Array();
53
+                var roastedNames = new Array();
54
+                while(query.next()) {
55
+                    roastedItems.push(query.value(0));
56
+                    roastedNames.push(query.value(1));
57
+                }
58
+                var recipes = new Array();
59
+                var recipeQuantities = new Array();
60
+                query.prepare("SELECT unroasted_id, unroasted_quantity FROM roasting_log WHERE roasted_id = :item AND time = (SELECT max(time) FROM roasting_log WHERE roasted_id = :item2)");
61
+                for(var i = 0; i < roastedItems.length; i++) {
62
+                    query.bind("item", roastedItems[i]);
63
+                    query.bind("item2", roastedItems[i]);
64
+                    query.exec();
65
+                    if(query.next()) {
66
+                        recipes.push(query.value(0));
67
+                        recipeQuantities.push(query.value(1));
68
+                    } else {
69
+                        recipes.push("{-1}");
70
+                        recipeQuantities.push("{-1}");
71
+                    }
72
+                }
73
+                query.prepare("SELECT min(unroasted_total_quantity / roasted_quantity), max(unroasted_total_quantity / roasted_quantity), avg(unroasted_total_quantity / roasted_quantity) FROM roasting_log WHERE roasted_id = :ri AND unroasted_id = :gi AND unroasted_total_quantity > 0 AND roasted_quantity > 0 AND approval = true");
74
+                var mins = new Array();
75
+                var maxes = new Array();
76
+                var means = new Array();
77
+                for(var i = 0; i < roastedItems.length; i++) {
78
+                    if(recipes[i] == "{-1}") {
79
+                        mins.push("undefined");
80
+                        maxes.push("undefined");
81
+                        means.push("undefined");
82
+                    } else {
83
+                        query.bind(":ri", Number(roastedItems[i]));
84
+                        query.bind(":gi", recipes[i]);
85
+                        query.exec();
86
+                        if(query.next()) {
87
+                            mins.push(query.value(0));
88
+                            maxes.push(query.value(1));
89
+                            means.push(query.value(2));
90
+                        } else {
91
+                            print("Error 2");
92
+                        }
93
+                    }
94
+                }
95
+                var proportionalCosts = new Array();
96
+                query.prepare("SELECT cost * :proportion * :conversion FROM purchase WHERE item = :id");
97
+                for(var i = 0; i < roastedItems.length; i++) {
98
+                    if(recipes[i] == "{-1}") {
99
+                        proportionalCosts.push("undefined");
100
+                    } else {
101
+                        greens = sqlToArray(recipes[i]);
102
+                        weights = sqlToArray(recipeQuantities[i]);
103
+                        proportions = new Array();
104
+                        quantitySum = weights.reduce(function(p, c) {
105
+                            return Number(p) + Number(c);
106
+                        });
107
+                        for(var j = 0; j < weights.length; j++) {
108
+                            proportions.push(weights[j]/quantitySum);
109
+                        }
110
+                        partialSum = 0;
111
+                        for(var j = 0; j < greens.length; j++) {
112
+                            query.bind(":proportion", proportions[j]);
113
+                            query.bind(":id", greens[j]);
114
+                            query.bind(":conversion", conversion);
115
+                            query.exec();
116
+                            if(query.next()) {
117
+                                partialSum += Number(query.value(0));
118
+                            } else {
119
+                                print("Error 3");
120
+                            }
121
+                        }
122
+                        proportionalCosts.push(partialSum);
123
+                    }
124
+                }
125
+                query = query.invalidate();
126
+                var minCosts = new Array();
127
+                var maxCosts = new Array();
128
+                var meanCosts = new Array();
129
+                for(var i = 0; i < roastedItems.length; i++) {
130
+                    if(recipes[i] == "{-1}") {
131
+                        minCosts.push("undefined");
132
+                        maxCosts.push("undefined");
133
+                        meanCosts.push("undefined");
134
+                    } else {
135
+                        minCosts.push(proportionalCosts[i] * mins[i]);
136
+                        maxCosts.push(proportionalCosts[i] * maxes[i]);
137
+                        meanCosts.push(proportionalCosts[i] * means[i]);
138
+                    }
139
+                }
140
+                output.writeStartElement("table");
141
+                output.writeAttribute("rules", "groups");
142
+                output.writeAttribute("cellpadding", "3px");
143
+                output.writeStartElement("thead");
144
+                output.writeStartElement("tr");
145
+                output.writeTextElement("th", TTR("greencost", "Coffee"));
146
+                output.writeTextElement("th", TTR("greencost", "Minimum Cost"));
147
+                output.writeTextElement("th", TTR("greencost", "Maximum Cost"));
148
+                output.writeTextElement("th", TTR("greencost", "Mean Cost"));
149
+                output.writeEndElement();
150
+                output.writeEndElement();
151
+                output.writeStartElement("tbody");
152
+                for(var i = 0; i < roastedItems.length; i++) {
153
+                    output.writeStartElement("tr");
154
+                    output.writeTextElement("td", roastedNames[i]);
155
+                    output.writeTextElement("td", Number(minCosts[i]).toFixed(2));
156
+                    output.writeTextElement("td", Number(maxCosts[i]).toFixed(2));
157
+                    output.writeTextElement("td", Number(meanCosts[i]).toFixed(2));
158
+                    output.writeEndElement();
159
+                }
160
+                output.writeEndElement();
161
+                output.writeEndElement();
162
+                output.writeEndElement();
163
+                output.writeEndElement();
164
+                output.writeEndDocument();
165
+                report.setContent(buffer);
166
+                buffer.close();
167
+            };
168
+            refresh();
169
+        ]]>
170
+    </program>
150
 </window>
171
 </window>
151
 
172
 

+ 83
- 28
config/Reports/dailyproductiondetail.xml View File

49
 				output.writeStartElement("html");
49
 				output.writeStartElement("html");
50
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
50
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
51
 				output.writeStartElement("head");
51
 				output.writeStartElement("head");
52
-				output.writeTextElement("title", "Daily Production Report");
52
+				output.writeTextElement("title", TTR("dailyproduction", "Daily Production Report"));
53
 				output.writeEndElement();
53
 				output.writeEndElement();
54
 				output.writeStartElement("body");
54
 				output.writeStartElement("body");
55
 				var dateString = "" + dateField.year() + "-" + dateField.month() + "-" + dateField.day();
55
 				var dateString = "" + dateField.year() + "-" + dateField.month() + "-" + dateField.day();
56
-				output.writeTextElement("h1", "Daily Production Report: " + dateString);
57
-				output.writeTextElement("h2", "Batches Roasted");
56
+				output.writeTextElement("h1", TTR("dailyproduction", "Daily Production Report: ") + dateString);
57
+				output.writeTextElement("h2", TTR("dailyproduction", "Batches Roasted"));
58
 				var query = new QSqlQuery();
58
 				var query = new QSqlQuery();
59
-				var q = "SELECT time, machine, (SELECT name FROM machine WHERE id = machine), (SELECT name FROM items WHERE id = roasted_id), unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_id, roasted_quantity, annotation, duration, files FROM roasting_log WHERE time > '" + dateString + "' AND time < ('" + dateString + "'::date + integer '1') ORDER BY time";
59
+				var q = "SELECT time, machine, (SELECT name FROM machine WHERE id = machine), (SELECT name FROM items WHERE id = roasted_id), unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_id, roasted_quantity, annotation, duration, files, (SELECT loss FROM roasting_specification WHERE item = roasted_id AND time = (SELECT max(time) FROM roasting_specification WHERE time <= roasting_log.time AND item = roasted_id)), (SELECT tolerance FROM roasting_specification WHERE item = roasted_id AND time = (SELECT max(time) FROM roasting_specification WHERE time <= roasting_log.time AND item = roasted_id)), (SELECT notes FROM roasting_specification WHERE item = roasted_id AND time = (SELECT max(time) FROM roasting_specification WHERE time <= roasting_log.time AND item = roasted_id)), approval FROM roasting_log WHERE time > '" + dateString + "' AND time < ('" + dateString + "'::date + integer '1') ORDER BY time";
60
 				query.exec(q);
60
 				query.exec(q);
61
 				var times = new Array();
61
 				var times = new Array();
62
 				var machines = new Array();
62
 				var machines = new Array();
65
 				output.writeAttribute("cellpadding", "3px");
65
 				output.writeAttribute("cellpadding", "3px");
66
 				output.writeStartElement("thead");
66
 				output.writeStartElement("thead");
67
 				output.writeStartElement("tr");
67
 				output.writeStartElement("tr");
68
-				output.writeTextElement("th", "Time");
69
-				output.writeTextElement("th", "Machine");
70
-				output.writeTextElement("th", "Batch ID");
68
+                                output.writeAttribute("valign", "bottom");
69
+				output.writeTextElement("th", TTR("dailyproduction", "Time"));
70
+				output.writeTextElement("th", TTR("dailyproduction", "Machine"));
71
+				output.writeTextElement("th", TTR("dailyproduction", "Batch ID"));
71
 				switch(unitBox.currentIndex)
72
 				switch(unitBox.currentIndex)
72
 				{
73
 				{
73
 					case 0:
74
 					case 0:
74
-						output.writeTextElement("th", "Green Weights (Kg)");
75
+						output.writeTextElement("th", TTR("dailyproduction", "Green Weights (Kg)"));
75
 						break;
76
 						break;
76
 					case 1:
77
 					case 1:
77
-						output.writeTextElement("th", "Green Weights (Lb)");
78
+						output.writeTextElement("th", TTR("dailyproduction", "Green Weights (Lb)"));
78
 						break;
79
 						break;
79
 				}
80
 				}
80
 				output.writeTextElement("th", "Green Coffees");
81
 				output.writeTextElement("th", "Green Coffees");
81
 				switch(unitBox.currentIndex)
82
 				switch(unitBox.currentIndex)
82
 				{
83
 				{
83
 					case 0:
84
 					case 0:
84
-						output.writeTextElement("th", "Roasted Weight (Kg)");
85
+						output.writeTextElement("th", TTR("dailyproduction", "Roasted Weight (Kg)"));
85
 						break;
86
 						break;
86
 					case 1:
87
 					case 1:
87
-						output.writeTextElement("th", "Roasted Weight (Lb)");
88
+						output.writeTextElement("th", TTR("dailyproduction", "Roasted Weight (Lb)"));
88
 						break;
89
 						break;
89
 				}
90
 				}
90
-				output.writeTextElement("th", "Roasted Coffee");
91
-				output.writeTextElement("th", "% Weight Loss");
92
-				output.writeTextElement("th", "Duration");
91
+				output.writeTextElement("th", TTR("dailyproduction", "Roasted Coffee"));
92
+				output.writeTextElement("th", TTR("dailyproduction", "% Weight Loss"));
93
+				output.writeTextElement("th", TTR("dailyproduction", "Duration"));
93
 				output.writeEndElement();
94
 				output.writeEndElement();
94
 				output.writeEndElement();
95
 				output.writeEndElement();
95
 				output.writeStartElement("tbody");
96
 				output.writeStartElement("tbody");
97
+                                output.writeAttribute("valign", "top");
96
 				while(query.next())
98
 				while(query.next())
97
 				{
99
 				{
98
 					times.push(query.value(0));
100
 					times.push(query.value(0));
164
 							output.writeCDATA(query.value(8));
166
 							output.writeCDATA(query.value(8));
165
 							break;
167
 							break;
166
 					}
168
 					}
167
-					output.writeTextElement("td", query.value(3) + " (" + query.value(7) + ")");
169
+                                        output.writeStartElement("td");
170
+                                        output.writeStartElement("span");
171
+                                        if(query.value(15) == "false") {
172
+                                            output.writeAttribute("style", "color:#FF0000");
173
+                                        }
174
+                                        output.writeCharacters(query.value(3) + " (" + query.value(7) + ")");
175
+                                        output.writeEndElement();
176
+                                        output.writeEndElement();
168
 					if(Number(query.value(6)) > 0) {
177
 					if(Number(query.value(6)) > 0) {
169
 						var loss = (Number(query.value(6)) - Number(query.value(8)))/Number(query.value(6));
178
 						var loss = (Number(query.value(6)) - Number(query.value(8)))/Number(query.value(6));
179
+                                                var lossMin;
180
+                                                var lossMax;
181
+                                                var lossColor;
182
+                                                var useLoss;
183
+                                                if(query.value(12) > 0) {
184
+                                                    useLoss = true;
185
+                                                    lossMin = Number(query.value(12)) - Number(query.value(13));
186
+                                                    lossMax = Number(query.value(12)) + Number(query.value(13));
187
+                                                    if(loss >= lossMin && loss <= lossMax) {
188
+                                                        lossColor = '#00FF00';
189
+                                                    } else {
190
+                                                        lossColor = '#FF0000';
191
+                                                    }
192
+                                                } else {
193
+                                                    lossColor = '#000000';
194
+                                                    useLoss = false;
195
+                                                }
170
 						loss *= 100;
196
 						loss *= 100;
171
-						output.writeTextElement("td", loss.toFixed(2)+"%");
197
+                                                var lossSpec = Number(query.value(12)) * 100;
198
+                                                var lossTol = Number(query.value(13)) * 100;
199
+                                                output.writeStartElement("td");
200
+                                                output.writeStartElement("span");
201
+                                                output.writeAttribute("style", "color:"+lossColor);
202
+                                                if(useLoss) {
203
+                                                    output.writeAttribute("title", lossSpec.toFixed(2) + "+/-" + lossTol.toFixed(2) + "%");
204
+                                                }
205
+                                                output.writeCharacters(loss.toFixed(2) + "%");
206
+                                                output.writeEndElement();
207
+                                                output.writeEndElement();
172
 					} else {
208
 					} else {
173
 						output.writeTextElement("td", "Undefined");
209
 						output.writeTextElement("td", "Undefined");
174
 					}
210
 					}
180
 						output.writeEmptyElement("td");
216
 						output.writeEmptyElement("td");
181
 						output.writeStartElement("td");
217
 						output.writeStartElement("td");
182
 						output.writeAttribute("colspan", "8");
218
 						output.writeAttribute("colspan", "8");
183
-						output.writeTextElement("em", query.value(9));
219
+                                                var noteArray = query.value(9).split("\n");
220
+                                                for(var i = 0; i < noteArray.length; i++) {
221
+                                                    output.writeStartElement("p");
222
+                                                    output.writeAttribute("style", "margin-top: 0; margin-bottom: 0");
223
+                                                    output.writeCharacters(noteArray[i]);
224
+                                                    output.writeEndElement();
225
+                                                }
184
 						output.writeEndElement();
226
 						output.writeEndElement();
185
 						output.writeEndElement();
227
 						output.writeEndElement();
186
 					}
228
 					}
188
 					var annotations = annotationFromRecord(files[0]);
230
 					var annotations = annotationFromRecord(files[0]);
189
 					output.writeStartElement("tr");
231
 					output.writeStartElement("tr");
190
 					output.writeStartElement("td");
232
 					output.writeStartElement("td");
191
-					output.writeAttribute("colspan", "9");
192
-					output.writeTextElement("strong", "Profile Summary");
233
+					output.writeAttribute("colspan", "5");
234
+					output.writeTextElement("strong", TTR("dailyproduction", "Profile Summary"));
193
 					var buffer2 = new QBuffer("<points>"+annotations+"</points>");
235
 					var buffer2 = new QBuffer("<points>"+annotations+"</points>");
194
 					buffer2.open(1);
236
 					buffer2.open(1);
195
 					var colQuery = new XQuery;
237
 					var colQuery = new XQuery;
198
 					var result = colQuery.exec();
240
 					var result = colQuery.exec();
199
 					buffer2.close();
241
 					buffer2.close();
200
 					var seriesHeaders = new Array();
242
 					var seriesHeaders = new Array();
201
-					seriesHeaders.push("Time");
243
+					seriesHeaders.push(TTR("dailyproduction", "Time"));
202
 					var records = result.split(";");
244
 					var records = result.split(";");
203
 					for(var i = 0; i < records.length - 1; i++) {
245
 					for(var i = 0; i < records.length - 1; i++) {
204
 						seriesHeaders.push(records[i].replace(/^\s+|\s+$/g,""));
246
 						seriesHeaders.push(records[i].replace(/^\s+|\s+$/g,""));
205
 					}
247
 					}
206
-					seriesHeaders.push("Note");
248
+					seriesHeaders.push(TTR("dailyproduction", "Note"));
207
 					output.writeStartElement("table");
249
 					output.writeStartElement("table");
208
 					output.writeStartElement("thead");
250
 					output.writeStartElement("thead");
209
 					output.writeStartElement("tr");
251
 					output.writeStartElement("tr");
265
 					output.writeEndElement();
307
 					output.writeEndElement();
266
 					output.writeEndElement();	
308
 					output.writeEndElement();	
267
 					output.writeEndElement();
309
 					output.writeEndElement();
310
+                                        output.writeStartElement("td");
311
+                                        output.writeAttribute("colspan", "4");
312
+                                        if(query.value(14)) {
313
+                                            output.writeTextElement("strong", "Roast Specification Notes");
314
+                                            var specArray = query.value(14).split("\n");
315
+                                            for(var i = 0; i < noteArray.length; i++) {
316
+                                                output.writeStartElement("p");
317
+                                                output.writeAttribute("style", "margin-top: 0; margin-bottom: 0");
318
+                                                output.writeCharacters(specArray[i]);
319
+                                                output.writeEndElement();
320
+                                            }                                            
321
+                                        }
322
+                                        output.writeEndElement();
268
 					output.writeEndElement();
323
 					output.writeEndElement();
269
 				}
324
 				}
270
 				output.writeEndElement();
325
 				output.writeEndElement();
273
 				output.writeEmptyElement("td");
328
 				output.writeEmptyElement("td");
274
 				output.writeEmptyElement("td");
329
 				output.writeEmptyElement("td");
275
 				output.writeStartElement("td");
330
 				output.writeStartElement("td");
276
-				output.writeTextElement("strong", "Totals:");
331
+				output.writeTextElement("strong", TTR("dailyproduction", "Totals:"));
277
 				output.writeEndElement();
332
 				output.writeEndElement();
278
 				q = "SELECT sum(unroasted_total_quantity), sum(roasted_quantity), sum(duration) FROM roasting_log WHERE time > '" + dateString + "' AND time < ('" + dateString + "'::date + integer '1')";
333
 				q = "SELECT sum(unroasted_total_quantity), sum(roasted_quantity), sum(duration) FROM roasting_log WHERE time > '" + dateString + "' AND time < ('" + dateString + "'::date + integer '1')";
279
 				query.exec(q);
334
 				query.exec(q);
303
 				output.writeEndElement();
358
 				output.writeEndElement();
304
 				output.writeEndElement(); //tfoot
359
 				output.writeEndElement(); //tfoot
305
 				output.writeEndElement(); //table
360
 				output.writeEndElement(); //table
306
-				output.writeTextElement("h2", "Inventory");
361
+				output.writeTextElement("h2", TTR("dailyproduction", "Inventory"));
307
 				output.writeStartElement("table");
362
 				output.writeStartElement("table");
308
 				output.writeAttribute("rules", "groups");
363
 				output.writeAttribute("rules", "groups");
309
 				output.writeAttribute("cellpadding", "3px");
364
 				output.writeAttribute("cellpadding", "3px");
310
 				output.writeStartElement("thead");
365
 				output.writeStartElement("thead");
311
 				output.writeStartElement("tr");
366
 				output.writeStartElement("tr");
312
-				output.writeTextElement("th", "Green Coffee");
313
-				output.writeTextElement("th", "Starting Inventory");
314
-				output.writeTextElement("th", "Ending Inventory");
315
-				output.writeTextElement("th", "Change");
316
-				output.writeTextElement("th", "Availability");
367
+				output.writeTextElement("th", TTR("dailyproduction", "Green Coffee"));
368
+				output.writeTextElement("th", TTR("dailyproduction", "Starting Inventory"));
369
+				output.writeTextElement("th", TTR("dailyproduction", "Ending Inventory"));
370
+				output.writeTextElement("th", TTR("dailyproduction", "Change"));
371
+				output.writeTextElement("th", TTR("dailyproduction", "Availability"));
317
 				output.writeEndElement();
372
 				output.writeEndElement();
318
 				output.writeEndElement();
373
 				output.writeEndElement();
319
 				output.writeStartElement("tbody");
374
 				output.writeStartElement("tbody");

+ 25
- 13
config/Reports/fypurchase.xml View File

14
 	</menu>
14
 	</menu>
15
 	<program>
15
 	<program>
16
 		<![CDATA[
16
 		<![CDATA[
17
-			this.windowTitle = "Typica - Coffee Purchase Previous Years Comparison";
17
+			this.windowTitle = TTR("fypurchase", "Typica - Coffee Purchase Previous Years Comparison");
18
 			/* Set Lifetime range. */
18
 			/* Set Lifetime range. */
19
 			var dateSelect = findChildObject(this, 'dates');
19
 			var dateSelect = findChildObject(this, 'dates');
20
 			var query = new QSqlQuery();
20
 			var query = new QSqlQuery();
21
-			query.exec("SELECT concat(EXTRACT(YEAR FROM time::date), '-01-01') FROM purchase WHERE time = (SELECT min(time) FROM purchase) UNION SELECT concat(EXTRACT(YEAR FROM 'now'::date), '-12-31')");
21
+			query.exec("SELECT concat(EXTRACT(YEAR FROM time::date), '-01-01') FROM purchase WHERE time = (SELECT min(time) FROM purchase) UNION SELECT concat(EXTRACT(YEAR FROM 'now'::date), '-12-31') ORDER BY concat ASC");
22
 			query.next();
22
 			query.next();
23
 			var lifetimeStartDate = query.value(0);
23
 			var lifetimeStartDate = query.value(0);
24
 			query.next();
24
 			query.next();
33
 			});
33
 			});
34
 			/* Add units to unit selector and enable functionality */
34
 			/* Add units to unit selector and enable functionality */
35
 			var unitBox = findChildObject(this, 'unit');
35
 			var unitBox = findChildObject(this, 'unit');
36
-			unitBox.addItem("Kg");
37
-			unitBox.addItem("Lb");
36
+			unitBox.addItem(TTR("fypurchase", "Kg"));
37
+			unitBox.addItem(TTR("fypurchase", "Lb"));
38
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
38
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
39
 			unitBox['currentIndexChanged(int)'].connect(function() {
39
 			unitBox['currentIndexChanged(int)'].connect(function() {
40
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
40
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
50
                 output.writeStartElement("html");
50
                 output.writeStartElement("html");
51
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
51
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
52
                 output.writeStartElement("head");
52
                 output.writeStartElement("head");
53
-				output.writeTextElement("title", "Coffee Purchase Previous Years Comparison");
53
+				output.writeTextElement("title", TTR("fypurchase", "Coffee Purchase Previous Years Comparison"));
54
 				output.writeEndElement();
54
 				output.writeEndElement();
55
 				output.writeStartElement("body");
55
 				output.writeStartElement("body");
56
-				output.writeTextElement("h1", "Coffee Purchase Previous Years Comparison");
56
+				output.writeTextElement("h1", TTR("fypurchase", "Coffee Purchase Previous Years Comparison"));
57
 				output.writeStartElement("table");
57
 				output.writeStartElement("table");
58
 				output.writeAttribute("style", "page-break-after: auto; text-align: left");
58
 				output.writeAttribute("style", "page-break-after: auto; text-align: left");
59
 				output.writeAttribute("rules", "groups");
59
 				output.writeAttribute("rules", "groups");
61
 				output.writeStartElement("thead");
61
 				output.writeStartElement("thead");
62
 				output.writeStartElement("tr");
62
 				output.writeStartElement("tr");
63
 				output.writeEmptyElement("th");
63
 				output.writeEmptyElement("th");
64
-				output.writeTextElement("th", "Sacks Purchased");
64
+				output.writeTextElement("th", TTR("fypurchase", "Sacks Purchased"));
65
 				switch(unitBox.currentIndex) {
65
 				switch(unitBox.currentIndex) {
66
 					case 0:
66
 					case 0:
67
-						output.writeTextElement("th", "Kilos Purchased");
67
+						output.writeTextElement("th", TTR("fypurchase", "Kg Purchased"));
68
 						break;
68
 						break;
69
 					case 1:
69
 					case 1:
70
-						output.writeTextElement("th", "Pounds Purchased");
70
+						output.writeTextElement("th", TTR("fypurchase", "Lb Purchased"));
71
 						break;
71
 						break;
72
 				}
72
 				}
73
-				output.writeTextElement("th", "Cost");
73
+				output.writeTextElement("th", TTR("fypurchase", "Cost"));
74
+                                switch(unitBox.currentIndex) {
75
+                                    case 0:
76
+                                        output.writeTextElement("th", TTR("fypurchase", "Cost per Kg"));
77
+                                        break;
78
+                                    case 1:
79
+                                        output.writeTextElement("th", TTR("fypurchase", "Cost per Lb"));
80
+                                        break;
81
+                                }
74
 				output.writeEndElement(); //tr
82
 				output.writeEndElement(); //tr
75
 				output.writeEndElement(); //thead
83
 				output.writeEndElement(); //thead
76
 				output.writeStartElement("tbody");
84
 				output.writeStartElement("tbody");
102
 					sacktotal += Number(query.value(0));
110
 					sacktotal += Number(query.value(0));
103
 					unittotal += Number(query.value(1));
111
 					unittotal += Number(query.value(1));
104
 					costtotal += Number(query.value(2));
112
 					costtotal += Number(query.value(2));
113
+                                        output.writeTextElement("td", (Number(query.value(2))/Number(query.value(1))).toFixed(2));
105
 					output.writeEndElement(); //tr
114
 					output.writeEndElement(); //tr
106
 				}
115
 				}
107
 				query = query.invalidate();
116
 				query = query.invalidate();
108
 				output.writeEndElement(); //tbody
117
 				output.writeEndElement(); //tbody
109
 				output.writeStartElement("tfoot");
118
 				output.writeStartElement("tfoot");
110
-				output.writeTextElement("th", "Totals:");
111
-				output.writeTextElement("td", sacktotal);
119
+				output.writeTextElement("th", TTR("fypurchase", "Totals:"));
120
+				output.writeTextElement("td", sacktotal.toFixed(2));
112
 				output.writeTextElement("td", unittotal.toFixed(2));
121
 				output.writeTextElement("td", unittotal.toFixed(2));
113
 				output.writeTextElement("td", costtotal.toFixed(2));
122
 				output.writeTextElement("td", costtotal.toFixed(2));
114
 				output.writeEndElement(); //tfoot
123
 				output.writeEndElement(); //tfoot
131
 					expandedYears.push(url);
140
 					expandedYears.push(url);
132
 					var element = new WebElement(view.findFirstElement("#" + url));
141
 					var element = new WebElement(view.findFirstElement("#" + url));
133
 					var year = url.slice(1,url.length);
142
 					var year = url.slice(1,url.length);
134
-					var details = '<tr><td /><td colspan="3"><table><tr><th>Id</th><th>Invoice</th><th>Vendor</th><th>Cost</th></tr>';
143
+					var details = '<tr><td /><td colspan="3"><table><tr><th>Id</th><th>' + TTR("fypurchase", "Invoice") +
144
+                                        '</th><th>' + TTR("fypurchase", "Vendor") +
145
+                                        '</th><th>' + TTR("fypurchase", "Cost") +
146
+                                        '</th></tr>';
135
 					q = "SELECT id, invoice, vendor, (SELECT sum(cost) FROM invoice_items WHERE invoice_id = id) FROM invoices WHERE time >= '" + year + "-01-01' AND time <= '" + year + "-12-31'";
147
 					q = "SELECT id, invoice, vendor, (SELECT sum(cost) FROM invoice_items WHERE invoice_id = id) FROM invoices WHERE time >= '" + year + "-01-01' AND time <= '" + year + "-12-31'";
136
 					var query = new QSqlQuery();
148
 					var query = new QSqlQuery();
137
 					query.exec(q);
149
 					query.exec(q);

+ 28
- 13
config/Reports/greensales.xml View File

14
 	</menu>
14
 	</menu>
15
 	<program>
15
 	<program>
16
 		<![CDATA[
16
 		<![CDATA[
17
-			this.windowTitle = "Typica - Green Coffee Sales";
17
+			this.windowTitle = TTR("greensales", "Typica - Green Coffee Sales");
18
 			var dateSelect = findChildObject(this, 'dates');
18
 			var dateSelect = findChildObject(this, 'dates');
19
 			var dateQuery = new QSqlQuery();
19
 			var dateQuery = new QSqlQuery();
20
 			dateQuery.exec("SELECT time::date FROM sale WHERE time = (SELECT min(time) FROM sale) OR time = (SELECT max(time) FROM sale) ORDER BY time ASC");
20
 			dateQuery.exec("SELECT time::date FROM sale WHERE time = (SELECT min(time) FROM sale) OR time = (SELECT max(time) FROM sale) ORDER BY time ASC");
29
 			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
29
 			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
30
 			dateQuery = dateQuery.invalidate();
30
 			dateQuery = dateQuery.invalidate();
31
 			var unitBox = findChildObject(this, 'unit');
31
 			var unitBox = findChildObject(this, 'unit');
32
-			unitBox.addItem("Kg");
33
-			unitBox.addItem("Lb");
32
+			unitBox.addItem(TTR("greensales", "Kg"));
33
+			unitBox.addItem(TTR("greensales", "Lb"));
34
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
34
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
35
 			unitBox['currentIndexChanged(int)'].connect(function() {
35
 			unitBox['currentIndexChanged(int)'].connect(function() {
36
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
36
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
46
 				var buffer = new QBuffer;
46
 				var buffer = new QBuffer;
47
 				buffer.open(3);
47
 				buffer.open(3);
48
 				var output = new XmlWriter(buffer);
48
 				var output = new XmlWriter(buffer);
49
-				var output = new XmlWriter(buffer);
50
 				output.writeStartDocument("1.0");
49
 				output.writeStartDocument("1.0");
51
 				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">');
50
 				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">');
52
 				output.writeStartElement("html");
51
 				output.writeStartElement("html");
53
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
52
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
54
 				output.writeStartElement("head");
53
 				output.writeStartElement("head");
55
-				output.writeTextElement("title", "Green Coffee Sales");
54
+				output.writeTextElement("title", TTR("greensales", "Green Coffee Sales"));
56
 				output.writeEndElement();
55
 				output.writeEndElement();
57
 				output.writeStartElement("body");
56
 				output.writeStartElement("body");
58
 				var dateRange = dateSelect.currentRange();
57
 				var dateRange = dateSelect.currentRange();
59
 				var startDate = dateRange[0];
58
 				var startDate = dateRange[0];
60
 				var endDate = dateRange[dateRange.length - 1];
59
 				var endDate = dateRange[dateRange.length - 1];
61
-				output.writeTextElement("h1", "Green Coffee Sales: " + startDate + " - " + endDate);
60
+				output.writeTextElement("h1", TTR("greensales", "Green Coffee Sales: ") + startDate + " - " + endDate);
62
 				var conversion = 1;
61
 				var conversion = 1;
63
-				var unitText = 'Lb';
62
+				var unitText = TTR("greensales", "Lb");
64
 				if(unitBox.currentIndex == 0) {
63
 				if(unitBox.currentIndex == 0) {
65
 					conversion = 2.2;
64
 					conversion = 2.2;
66
-					unitText = 'Kg';
65
+					unitText = TTR("greensales", "Kg");
67
 				}
66
 				}
68
 				var query = new QSqlQuery();
67
 				var query = new QSqlQuery();
69
 				query.prepare("SELECT item, (SELECT name FROM coffees WHERE id = item) AS name, (SELECT origin FROM coffees WHERE id = item) AS origin, (SELECT reference FROM coffees WHERE id = item) AS reference, (SUM(quantity)/:conversion)::numeric(12,3) FROM sale WHERE time < :ed ::date + interval '1 day' AND time >= :sd GROUP BY item ORDER BY name ASC");
68
 				query.prepare("SELECT item, (SELECT name FROM coffees WHERE id = item) AS name, (SELECT origin FROM coffees WHERE id = item) AS origin, (SELECT reference FROM coffees WHERE id = item) AS reference, (SUM(quantity)/:conversion)::numeric(12,3) FROM sale WHERE time < :ed ::date + interval '1 day' AND time >= :sd GROUP BY item ORDER BY name ASC");
76
 				output.writeAttribute("cellpadding", "3px");
75
 				output.writeAttribute("cellpadding", "3px");
77
 				output.writeStartElement("thead");
76
 				output.writeStartElement("thead");
78
 				output.writeStartElement("tr");
77
 				output.writeStartElement("tr");
79
-				output.writeTextElement("th", "ID"); // 0
80
-				output.writeTextElement("th", "Coffee"); // 1
81
-				output.writeTextElement("th", "Origin"); // 2
82
-				output.writeTextElement("th", "Reference"); // 3
83
-				output.writeTextElement("th", "Quantity"); // 4
78
+				output.writeTextElement("th", TTR("greensales", "ID")); // 0
79
+				output.writeTextElement("th", TTR("greensales", "Coffee")); // 1
80
+				output.writeTextElement("th", TTR("greensales", "Origin")); // 2
81
+				output.writeTextElement("th", TTR("greensales", "Reference")); // 3
82
+				output.writeTextElement("th", TTR("greensales", "Quantity")); // 4
84
 				output.writeEndElement();
83
 				output.writeEndElement();
85
 				output.writeEndElement();
84
 				output.writeEndElement();
86
 				output.writeStartElement("tbody");
85
 				output.writeStartElement("tbody");
96
 					output.writeEndElement();
95
 					output.writeEndElement();
97
 				}
96
 				}
98
 				output.writeEndElement();
97
 				output.writeEndElement();
98
+                                output.writeStartElement("tfoot");
99
+                                query.prepare("SELECT (sum(quantity)/:conversion)::numeric(12,3) FROM sale WHERE time < :ed ::date + interval '1 day' AND time >= :sd");
100
+                                query.bind(":conversion", conversion);
101
+                                query.bind(":ed", endDate);
102
+                                query.bind(":sd", startDate);
103
+                                query.exec();
104
+                                if(query.next()) {
105
+                                    output.writeStartElement("tr");
106
+                                    output.writeEmptyElement("td");
107
+                                    output.writeEmptyElement("td");
108
+                                    output.writeEmptyElement("td");
109
+                                    output.writeTextElement("th", TTR("greensales", "Total:"));
110
+                                    output.writeTextElement("td", query.value(0));
111
+                                    output.writeEndElement();
112
+                                }
113
+                                output.writeEndElement();
99
 				output.writeEndElement();
114
 				output.writeEndElement();
100
 				output.writeEndElement();
115
 				output.writeEndElement();
101
 				output.writeEndElement();
116
 				output.writeEndElement();

+ 320
- 0
config/Reports/historyreport.xml View File

1
+<window id="batchreport">
2
+    <reporttitle>Production:->Batch Log</reporttitle>
3
+    <layout type="vertical">
4
+        <layout type="horizontal">
5
+            <daterange id="dates" initial="6" /><!-- Last 7 Days -->
6
+            <label>Batch Type: </label>
7
+            <sqldrop id="batchtype" />
8
+            <label>Approval: </label>
9
+            <sqldrop id="approval" />
10
+            <label>Search: </label>
11
+            <line id="search" />
12
+            <label>Weight Unit:</label>
13
+            <sqldrop id="unit" />
14
+            <stretch />
15
+        </layout>
16
+        <webview id="report" />
17
+    </layout>
18
+    <menu name="File">
19
+        <item id="print" shortcut="Ctrl+P">Print...</item>
20
+    </menu>
21
+    <program>
22
+        <![CDATA[
23
+            this.setWindowTitle(TTR("batchreport", "Typica - Batch Log"));
24
+            var dateSelect = findChildObject(this, 'dates');
25
+            var dateQuery = new QSqlQuery();
26
+            dateQuery.exec("SELECT time::date FROM roasting_log WHERE time = (SELECT min(time) FROM roasting_log) OR time = (SELECT max(time) FROM roasting_log) ORDER BY time ASC");
27
+            dateQuery.next();
28
+            var lifetimeStartDate = dateQuery.value(0);
29
+            var lifetimeEndDate;
30
+            if(dateQuery.next()) {
31
+                lifetimeEndDate = dateQuery.value(0);
32
+            } else {
33
+                lifetimeEndDate = lifetimeStartDate;
34
+            }
35
+            dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
36
+            dateQuery = dateQuery.invalidate();
37
+            dateSelect.currentIndex = QSettings.value("script/history/dateIndex", 6);
38
+            dateSelect.rangeUpdated.connect(function() {
39
+                if(dateSelect.currentIndex != 24) {
40
+                    QSettings.setValue("script/history/dateIndex", dateSelect.currentIndex);
41
+                }
42
+                refresh();
43
+            });
44
+            var unitBox = findChildObject(this, 'unit');
45
+            unitBox.addItem(TTR("batchreport", "Kg"));
46
+            unitBox.addItem(TTR("batchreport", "Lb"));
47
+            unitBox.currentIndex = QSettings.value("script/history_unit", 1);
48
+            unitBox['currentIndexChanged(int)'].connect(function() {
49
+                QSettings.setValue("script/history_unit", unitBox.currentIndex);
50
+                refresh();
51
+            });
52
+            var batchType = findChildObject(this, 'batchtype');
53
+            batchType.addItem(TTR("batchreport", "Any"));
54
+            batchType.addItem(TTR("batchreport", "Production Roasts"));
55
+            batchType.addItem(TTR("batchreport", "Sample Roasts"));
56
+            batchType.currentIndex = QSettings.value("script/history/batchtypefilter", 1);
57
+            batchType['currentIndexChanged(int)'].connect(function() {
58
+                QSettings.setValue("script/history/batchtypefilter", batchType.currentIndex);
59
+                refresh();
60
+            });
61
+            var approval = findChildObject(this, 'approval');
62
+            approval.addItem(TTR("batchreport", "Any"));
63
+            approval.addItem(TTR("batchreport", "Approved"));
64
+            approval.addItem(TTR("batchreport", "Not Approved"));
65
+            approval.currentIndex = QSettings.value("script/history/approvalfilter", 1);
66
+            approval['currentIndexChanged(int)'].connect(function() {
67
+                QSettings.setValue("script/history/approvalfilter", approval.currentIndex);
68
+                refresh();
69
+            });
70
+            var search = findChildObject(this, 'search');
71
+            search.editingFinished.connect(function() {
72
+                refresh();
73
+            });
74
+            var view = findChildObject(this, 'report');
75
+            var printMenu = findChildObject(this, 'print');
76
+            printMenu.triggered.connect(function() {
77
+                view.print();
78
+            });
79
+            view.scriptLinkClicked.connect(function(url) {
80
+                var arg = decodeURI(url.slice(2, url.length));
81
+                var key = arg.split("@");
82
+                var details = createWindow("batchDetails");
83
+                var fakeTable = new Object;
84
+                fakeTable.holding = new Array(7);
85
+                fakeTable.data = function(r, c) {
86
+                    return this.holding[c];
87
+                };
88
+                var conversion = 1;
89
+                if(unitBox.currentIndex == 0) {
90
+                    conversion = 2.2;
91
+                }
92
+                var query = new QSqlQuery();
93
+                var q = "SELECT time, machine, (SELECT name FROM items WHERE id = roasted_id) AS name, unroasted_total_quantity / " + conversion + " AS green, roasted_quantity  / " + conversion + " AS roasted, CASE WHEN unroasted_total_quantity = 0 THEN NULL ELSE ((unroasted_total_quantity - roasted_quantity) / unroasted_total_quantity * 100::numeric)::numeric(12,2) END AS weight_loss, duration, annotation FROM roasting_log WHERE machine = :machine AND time = :time";
94
+                query.prepare(q);
95
+                query.bind(":machine", key[0]);
96
+                query.bind(":time", key[1]);
97
+                query.exec();
98
+                query.next();
99
+                for(var i = 0; i < 8; i++) {
100
+                    fakeTable.holding[i] = query.value(i);
101
+                }
102
+                fakeTable.holding[0] = key[1];
103
+                query = query.invalidate();
104
+                details.loadData(fakeTable, 0);
105
+            });
106
+            var refresh = function() {
107
+                var dateRange = dateSelect.currentRange();
108
+                var startDate = dateRange[0];
109
+                var endDate = dateRange[dateRange.length - 1];
110
+                var conversion = 1;
111
+                if(unitBox.currentIndex == 0) {
112
+                    conversion = 2.2;
113
+                }
114
+                var approvalClause = "";
115
+                switch(approval.currentIndex) {
116
+                    case 1:
117
+                        approvalClause = " AND approval = true";
118
+                        break;
119
+                    case 2:
120
+                        approvalClause = " AND approval = false";
121
+                        break;
122
+                }
123
+                var batchClause = "";
124
+                switch(batchType.currentIndex) {
125
+                    case 1:
126
+                        batchClause = " AND transaction_type = 'ROAST'";
127
+                        break;
128
+                    case 2:
129
+                        batchClause = " AND transaction_type = 'SAMPLEROAST'";
130
+                        break;
131
+                }
132
+                var searchClause = "";
133
+                if(search.text.length > 0) {
134
+                    searchClause = " WHERE (person ~* :p1 OR rname ~* :p2 OR mname ~* :p3 OR greens ~* :p4 OR annotation ~* :p5 OR array_to_string(files, ',') ~* :p6)";
135
+                }
136
+                var q = "WITH qq AS (SELECT roasting_log.time, array_to_string(files, ','), person, (SELECT name || ' (' || id || ')' FROM items WHERE id = roasted_id) AS rname, duration, (SELECT name FROM machine WHERE id = machine) AS mname, array_to_string(ARRAY(SELECT name || ' (' || id || ')' FROM items WHERE id IN (SELECT unnest(unroasted_id))), ',') AS greens, (unroasted_total_quantity/:c1)::numeric(12,2), (roasted_quantity/:c2)::numeric(12,2), loss, annotation, approval, spec_loss::numeric(12,2) || '±' || spec_tolerance::numeric(12,2) AS lspec, notes, loss_match, machine || '@' || roasting_log.time AS link, files FROM roasting_log, LATERAL (SELECT CASE WHEN (unroasted_total_quantity = 0) THEN NULL ELSE (((unroasted_total_quantity - roasted_quantity)/unroasted_total_quantity)*100)::numeric(12,2) END AS loss) lc, LATERAL (WITH q AS (SELECT (SELECT min(time) - interval '10 years' FROM roasting_log) AS time, NULL::numeric AS loss, NULL::numeric AS tolerance, NULL::text AS notes) SELECT time, (loss*100)::numeric(12,2) AS spec_loss, (tolerance*100)::numeric(12,2) AS spec_tolerance, notes FROM roasting_specification WHERE item = roasted_id AND time = (SELECT max(time) FROM roasting_specification WHERE time <= roasting_log.time AND item = roasted_id) UNION (SELECT * FROM q) ORDER BY time DESC LIMIT 1) spec, LATERAL (SELECT loss >= spec_loss - spec_tolerance AND loss <= spec_loss + spec_tolerance AS loss_match) m WHERE roasting_log.time >= :sd AND roasting_log.time < :ed::date + interval '1 day'" + approvalClause + batchClause + ") SELECT * FROM qq" + searchClause + " ORDER BY time DESC";
137
+                var query = new QSqlQuery();
138
+                query.prepare(q);
139
+                query.bind(":c1", conversion);
140
+                query.bind(":c2", conversion);
141
+                query.bind(":sd", startDate);
142
+                query.bind(":ed", endDate);
143
+                if(searchClause.length > 0) {
144
+                    var pattern = ".*" + search.text + ".*";
145
+                    query.bind(":p1", pattern);
146
+                    query.bind(":p2", pattern);
147
+                    query.bind(":p3", pattern);
148
+                    query.bind(":p4", pattern);
149
+                    query.bind(":p5", pattern);
150
+                    query.bind(":p6", pattern);
151
+                }
152
+                query.exec();
153
+                var buffer = new QBuffer;
154
+                buffer.open(3);
155
+                var output = new XmlWriter(buffer);
156
+                output.writeStartDocument("1.0");
157
+                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">');
158
+                output.writeStartElement("html");
159
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
160
+                output.writeStartElement("head");
161
+                output.writeTextElement("title", TTR("batchreport", "Batch Log"));
162
+                output.writeEndElement();
163
+                output.writeStartElement("body");
164
+                output.writeStartElement("table");
165
+                output.writeAttribute("style", "page-break-after: auto; text-align: left");
166
+                output.writeAttribute("rules", "groups");
167
+                output.writeAttribute("cellpadding", "3px");
168
+                output.writeStartElement("thead");
169
+                output.writeStartElement("tr");
170
+                output.writeAttribute("valign", "bottom");
171
+                output.writeTextElement("th", TTR("batchreport", "Time"));
172
+                output.writeTextElement("th", TTR("batchreport", "File Numbers"));
173
+                output.writeTextElement("th", TTR("batchreport", "Operator"));
174
+                output.writeTextElement("th", TTR("batchreport", "Roasted Coffee"));
175
+                output.writeEndElement();
176
+                output.writeStartElement("tr");
177
+                output.writeAttribute("valign", "bottom");
178
+                output.writeEmptyElement("td");
179
+                output.writeTextElement("th", TTR("batchreport", "Duration"));
180
+                output.writeTextElement("th", TTR("batchreport", "Machine"));
181
+                output.writeTextElement("th", TTR("batchreport", "Green Coffees"));
182
+                output.writeEndElement();
183
+                output.writeStartElement("tr");
184
+                output.writeAttribute("valign", "bottom");
185
+                output.writeEmptyElement("td");
186
+                output.writeTextElement("th", TTR("batchreport", "Green Weight"));
187
+                output.writeTextElement("th", TTR("batchreport", "Roasted Weight"));
188
+                output.writeTextElement("th", TTR("batchreport", "% Weight Loss"));
189
+                output.writeEndElement();
190
+                output.writeStartElement("tr");
191
+                output.writeAttribute("valign", "bottom");
192
+                output.writeEmptyElement("td");
193
+                output.writeStartElement("th");
194
+                output.writeAttribute("colspan", "2");
195
+                output.writeCharacters(TTR("batchreport", "Batch Notes"));
196
+                output.writeEndElement();
197
+                output.writeTextElement("th", TTR("batchreport", "Specification Notes"));
198
+                output.writeEndElement();
199
+                output.writeEndElement();
200
+                output.writeStartElement("tbody");
201
+                while(query.next()) {
202
+                    output.writeStartElement("tr");
203
+                    output.writeAttribute("valign", "top");
204
+                    output.writeStartElement("td");
205
+                    output.writeStartElement("a");
206
+                    output.writeAttribute("href", "typica://script/b/" + query.value(15));
207
+                    output.writeCharacters(query.value(0).replace("T", " "));
208
+                    output.writeEndElement();
209
+                    output.writeEndElement();
210
+                    output.writeStartElement("td");
211
+                    output.writeStartElement("a");
212
+                    output.writeAttribute("href", "typica://script/b/" + query.value(15));
213
+                    output.writeCharacters(query.value(1));
214
+                    output.writeEndElement();
215
+                    output.writeEndElement();
216
+                    output.writeTextElement("td", query.value(2));
217
+                    output.writeStartElement("td");
218
+                    output.writeStartElement("span");
219
+                    if(query.value(11) == "false") {
220
+                        output.writeAttribute("style", "color:#FF0000");
221
+                    }
222
+                    output.writeCharacters(query.value(3));
223
+                    output.writeEndElement();
224
+                    output.writeEndElement();
225
+                    output.writeEndElement();
226
+                    output.writeStartElement("tr");
227
+                    output.writeAttribute("valign", "top");
228
+                    output.writeEmptyElement("td");
229
+                    output.writeTextElement("td", query.value(4));
230
+                    output.writeTextElement("td", query.value(5));
231
+                    output.writeTextElement("td", query.value(6));
232
+                    output.writeEndElement();
233
+                    output.writeStartElement("tr");
234
+                    output.writeAttribute("valign", "top");
235
+                    output.writeEmptyElement("td");
236
+                    output.writeTextElement("td", query.value(7));
237
+                    output.writeTextElement("td", query.value(8));
238
+                    output.writeStartElement("td");
239
+                    output.writeStartElement("span");
240
+                    if(query.value(14) == "false" && query.value(12).length > 0) {
241
+                        output.writeAttribute("style", "color:#FF0000");
242
+                    } else if (query.value(14) === "true") {
243
+                        output.writeAttribute("style", "color:#00FF00");
244
+                    }
245
+                    output.writeAttribute("title", query.value(12));
246
+                    output.writeCharacters(query.value(9));
247
+                    output.writeEndElement();
248
+                    output.writeEndElement();
249
+                    output.writeEndElement();
250
+                    output.writeStartElement("tr");
251
+                    output.writeAttribute("valign", "top");
252
+                    output.writeEmptyElement("td");
253
+                    output.writeStartElement("td");
254
+                    output.writeAttribute("colspan", "2");
255
+                    output.writeAttribute("style", "max-width: 400px");
256
+                    var noteArray = query.value(10).split("\n");
257
+                    for(var i = 0; i < noteArray.length; i++) {
258
+                        output.writeStartElement("p");
259
+                        output.writeAttribute("style", "margin-top: 0; margin-bottom: 0");
260
+                        output.writeCharacters(noteArray[i]);
261
+                        output.writeEndElement();
262
+                    }
263
+                    output.writeEndElement();
264
+                    output.writeStartElement("td");
265
+                    output.writeAttribute("style", "max-width: 400px");
266
+                    var specArray = query.value(13).split("\n");
267
+                    for(var i = 0; i < specArray.length; i++) {
268
+                        output.writeStartElement("p", specArray[i]);
269
+                        output.writeAttribute("style", "margin-top: 0; margin-bottom: 0");
270
+                        output.writeCharacters(specArray[i]);
271
+                        output.writeEndElement();
272
+                    }
273
+                    output.writeEndElement();
274
+                    output.writeEndElement();
275
+                }
276
+                output.writeEndElement();
277
+                output.writeStartElement("tfoot");
278
+                output.writeStartElement("tr");
279
+                output.writeAttribute("valign", "bottom");
280
+                output.writeTextElement("th", TTR("batchreport", "Total Batches"));
281
+                output.writeTextElement("th", TTR("batchreport", "Total Duration"));
282
+                output.writeTextElement("th", TTR("batchreport", "Total Green Weight"));
283
+                output.writeTextElement("th", TTR("batchreport", "Total Roasted Weight"));
284
+                output.writeEndElement();
285
+                output.writeStartElement("tr");
286
+                output.writeAttribute("valign", "top");
287
+                q = "WITH qq AS (SELECT roasting_log.time, array_to_string(files, ','), person, (SELECT name || ' (' || id || ')' FROM items WHERE id = roasted_id) AS rname, duration, (SELECT name FROM machine WHERE id = machine) AS mname, array_to_string(ARRAY(SELECT name || ' (' || id || ')' FROM items WHERE id IN (SELECT unnest(unroasted_id))), ',') AS greens, (unroasted_total_quantity/:c1)::numeric(12,2) AS uq, (roasted_quantity/:c2)::numeric(12,2) AS rq, annotation, approval, files FROM roasting_log WHERE roasting_log.time >= :sd AND roasting_log.time < :ed::date + interval '1 day'" + approvalClause + batchClause + ") SELECT count(1), sum(duration), sum(uq), sum(rq) FROM qq" + searchClause;
288
+                var query = new QSqlQuery();
289
+                query.prepare(q);
290
+                query.bind(":c1", conversion);
291
+                query.bind(":c2", conversion);
292
+                query.bind(":sd", startDate);
293
+                query.bind(":ed", endDate);
294
+                if(searchClause.length > 0) {
295
+                    var pattern = ".*" + search.text + ".*";
296
+                    query.bind(":p1", pattern);
297
+                    query.bind(":p2", pattern);
298
+                    query.bind(":p3", pattern);
299
+                    query.bind(":p4", pattern);
300
+                    query.bind(":p5", pattern);
301
+                    query.bind(":p6", pattern);
302
+                }
303
+                query.exec();
304
+                query.next();
305
+                for(var i = 0; i < 4; i++) {
306
+                    output.writeTextElement("td", query.value(i));
307
+                }
308
+                output.writeEndElement();
309
+                output.writeEndElement();
310
+                query = query.invalidate();
311
+                output.writeEndElement();
312
+                output.writeEndElement();
313
+                output.writeEndDocument();
314
+                view.setContent(buffer);
315
+                buffer.close();
316
+            };
317
+            refresh();
318
+        ]]>
319
+    </program>
320
+</window>

+ 24
- 25
config/Reports/invchange.xml View File

14
 	</menu>
14
 	</menu>
15
 	<program>
15
 	<program>
16
 		<![CDATA[
16
 		<![CDATA[
17
-			this.windowTitle = "Typica - Inventory Change Summary";
17
+			this.windowTitle = TTR("invchange", "Typica - Inventory Change Summary");
18
 			var dateSelect = findChildObject(this, 'dates');
18
 			var dateSelect = findChildObject(this, 'dates');
19
 			var dateQuery = new QSqlQuery();
19
 			var dateQuery = new QSqlQuery();
20
 			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");
20
 			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");
29
 			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
29
 			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
30
 			dateQuery = dateQuery.invalidate();
30
 			dateQuery = dateQuery.invalidate();
31
 			var unitBox = findChildObject(this, 'unit');
31
 			var unitBox = findChildObject(this, 'unit');
32
-			unitBox.addItem("Kg");
33
-			unitBox.addItem("Lb");
32
+			unitBox.addItem(TTR("invchange", "Kg"));
33
+			unitBox.addItem(TTR("invchange", "Lb"));
34
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
34
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
35
 			unitBox['currentIndexChanged(int)'].connect(function() {
35
 			unitBox['currentIndexChanged(int)'].connect(function() {
36
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
36
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
50
 				output.writeStartElement("html");
50
 				output.writeStartElement("html");
51
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
51
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
52
 				output.writeStartElement("head");
52
 				output.writeStartElement("head");
53
-				output.writeTextElement("title", "Inventory Change Summary");
53
+				output.writeTextElement("title", TTR("invchange", "Inventory Change Summary"));
54
 				output.writeEndElement();
54
 				output.writeEndElement();
55
 				output.writeStartElement("body");
55
 				output.writeStartElement("body");
56
 				var dateRange = dateSelect.currentRange();
56
 				var dateRange = dateSelect.currentRange();
57
 				var startDate = dateRange[0];
57
 				var startDate = dateRange[0];
58
 				var endDate = dateRange[dateRange.length - 1];
58
 				var endDate = dateRange[dateRange.length - 1];
59
-				output.writeTextElement("h1", "Inventory Change Summary: " + startDate + " – " + endDate);
59
+				output.writeTextElement("h1", TTR("invchange", "Inventory Change Summary: ") + startDate + " – " + endDate);
60
 				var conversion = 1;
60
 				var conversion = 1;
61
 				if(unitBox.currentIndex == 0) {
61
 				if(unitBox.currentIndex == 0) {
62
 					conversion = 2.2;
62
 					conversion = 2.2;
63
 				}
63
 				}
64
-				var unitText = "Lb";
64
+				var unitText = TTR("invchange", "Lb");
65
 				if(unitBox.currentIndex == 0) {
65
 				if(unitBox.currentIndex == 0) {
66
-					unitText = "Kg";
66
+					unitText = TTR("invchange", "Kg");
67
 				}
67
 				}
68
 				var query = new QSqlQuery();
68
 				var query = new QSqlQuery();
69
-					var q = "WITH q AS (SELECT id, name, reference, COALESCE((SELECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :sd1)), 0)/:c1 AS starting_balance, COALESCE((SELECT sum(quantity) FROM purchase WHERE item = id AND time >= :sd2 AND time < :ed1 ::date + interval '1 day'), 0)/:c2 AS purchase, COALESCE((SELECT sum(quantity) FROM use WHERE item = id AND time >= :sd3 AND time < :ed2 ::date + interval '1 day'), 0)/:c3 AS use, COALESCE((SELECT sum(quantity) FROM sale WHERE item = id AND time >= :sd4 AND time < :ed3 ::date + interval '1 day'), 0)/:c4 AS sale, (SElECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :ed4 ::date + interval '1 day'))/:c5 AS quantity, (SELECT sum(cost * quantity) / sum(quantity) FROM purchase WHERE item = id) AS unit_cost FROM coffees WHERE id IN (SELECT item FROM purchase WHERE time >= :sd6 AND time < :ed5 ::date + interval '1 day') OR id IN (SELECT id FROM items WHERE (SELECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :ed6 ::date + interval '1 day')) > 0) OR id IN (SELECT DISTINCT item FROM all_transactions WHERE time > :sd7 AND time < :ed7 ::date + interval '1 day')) SELECT *, (starting_balance + purchase - use - sale - quantity)/:c7 AS adjustment, starting_balance * unit_cost * :c8 AS starting_cost, purchase * unit_cost * :c9 AS purchase_cost, use * unit_cost * :c10 AS use_cost, sale * unit_cost * :c11 AS sale_cost, quantity * unit_cost * :c12 AS quantity_cost, (starting_balance + purchase - use - sale - quantity) * unit_cost * :c13 AS adjustment_cost, (SELECT sum(quantity)/:c6 FROM purchase WHERE item = id) AS total_purchase FROM q ORDER BY name";
69
+					var q = "WITH q AS (SELECT id, name, reference, COALESCE((SELECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :sd1)), 0)/:c1 AS starting_balance, COALESCE((SELECT sum(quantity) FROM purchase WHERE item = id AND time >= :sd2 AND time < :ed1 ::date + interval '1 day'), 0)/:c2 AS purchase, COALESCE((SELECT sum(quantity) FROM use WHERE item = id AND time >= :sd3 AND time < :ed2 ::date + interval '1 day'), 0)/:c3 AS use, COALESCE((SELECT sum(quantity) FROM sale WHERE item = id AND time >= :sd4 AND time < :ed3 ::date + interval '1 day'), 0)/:c4 AS sale, (SElECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :ed4 ::date + interval '1 day'))/:c5 AS quantity, (SELECT sum(cost * quantity) / sum(quantity) FROM purchase WHERE item = id) AS unit_cost FROM coffees WHERE id IN (SELECT item FROM purchase WHERE time >= :sd6 AND time < :ed5 ::date + interval '1 day') OR id IN (SELECT id FROM items WHERE (SELECT balance FROM item_history(id) WHERE time = (SELECT max(time) FROM item_history(id) WHERE time < :ed6 ::date + interval '1 day')) > 0) OR id IN (SELECT DISTINCT item FROM all_transactions WHERE time > :sd7 AND time < :ed7 ::date + interval '1 day')) SELECT *, (starting_balance + purchase - use - sale - quantity) AS adjustment, starting_balance * unit_cost * :c8 AS starting_cost, purchase * unit_cost * :c9 AS purchase_cost, use * unit_cost * :c10 AS use_cost, sale * unit_cost * :c11 AS sale_cost, quantity * unit_cost * :c12 AS quantity_cost, (starting_balance + purchase - use - sale - quantity) * unit_cost * :c13 AS adjustment_cost, (SELECT sum(quantity)/:c6 FROM purchase WHERE item = id) AS total_purchase FROM q ORDER BY name";
70
 				query.prepare(q);
70
 				query.prepare(q);
71
 				query.bind(":sd1", startDate);
71
 				query.bind(":sd1", startDate);
72
 				query.bind(":sd2", startDate);
72
 				query.bind(":sd2", startDate);
87
 				query.bind(":c4", conversion);
87
 				query.bind(":c4", conversion);
88
 				query.bind(":c5", conversion);
88
 				query.bind(":c5", conversion);
89
 				query.bind(":c6", conversion);
89
 				query.bind(":c6", conversion);
90
-				query.bind(":c7", conversion);
91
 				query.bind(":c8", conversion);
90
 				query.bind(":c8", conversion);
92
 				query.bind(":c9", conversion);
91
 				query.bind(":c9", conversion);
93
 				query.bind(":c10", conversion);
92
 				query.bind(":c10", conversion);
100
 				output.writeAttribute("cellpadding", "3px");
99
 				output.writeAttribute("cellpadding", "3px");
101
 				output.writeStartElement("thead");
100
 				output.writeStartElement("thead");
102
 				output.writeStartElement("tr");
101
 				output.writeStartElement("tr");
103
-				output.writeTextElement("th", "ID"); // 0
104
-				output.writeTextElement("th", "Coffee"); // 1
105
-				output.writeTextElement("th", "Reference"); // 2
106
-				output.writeTextElement("th", "Starting (" + unitText + ")"); // 3
107
-				output.writeTextElement("th", "Cost"); // 10
108
-				output.writeTextElement("th", "Purchase (" + unitText + ")"); // 4
109
-				output.writeTextElement("th", "Cost"); // 11
110
-				output.writeTextElement("th", "Use (" + unitText + ")"); // 5
111
-				output.writeTextElement("th", "Cost"); // 12
112
-				output.writeTextElement("th", "Sale (" + unitText + ")"); // 6
113
-				output.writeTextElement("th", "Cost"); // 13
114
-				output.writeTextElement("th", "Adjustment (" + unitText + ")"); // 9
115
-				output.writeTextElement("th", "Cost"); // 15
116
-				output.writeTextElement("th", "Ending (" + unitText + ")"); // 7
117
-				output.writeTextElement("th", "Cost"); // 14
102
+				output.writeTextElement("th", TTR("invchange", "ID")); // 0
103
+				output.writeTextElement("th", TTR("invchange", "Coffee")); // 1
104
+				output.writeTextElement("th", TTR("invchange", "Reference")); // 2
105
+				output.writeTextElement("th", TTR("invchange", "Starting (") + unitText + ")"); // 3
106
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 10
107
+				output.writeTextElement("th", TTR("invchange", "Purchase (") + unitText + ")"); // 4
108
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 11
109
+				output.writeTextElement("th", TTR("invchange", "Use (") + unitText + ")"); // 5
110
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 12
111
+				output.writeTextElement("th", TTR("invchange", "Sale (") + unitText + ")"); // 6
112
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 13
113
+				output.writeTextElement("th", TTR("invchange", "Adjustment (") + unitText + ")"); // 9
114
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 15
115
+				output.writeTextElement("th", TTR("invchange", "Ending (") + unitText + ")"); // 7
116
+				output.writeTextElement("th", TTR("invchange", "Cost")); // 14
118
 				output.writeEndElement();
117
 				output.writeEndElement();
119
 				output.writeEndElement();
118
 				output.writeEndElement();
120
 				output.writeStartElement("tbody");
119
 				output.writeStartElement("tbody");
190
 				output.writeStartElement("tr");
189
 				output.writeStartElement("tr");
191
 				output.writeTextElement("td", "");
190
 				output.writeTextElement("td", "");
192
 				output.writeTextElement("td", "");
191
 				output.writeTextElement("td", "");
193
-				output.writeTextElement("th", "Total:");
192
+				output.writeTextElement("th", TTR("invchange", "Total:"));
194
 				output.writeTextElement("td", sum3.toFixed(2));
193
 				output.writeTextElement("td", sum3.toFixed(2));
195
 				output.writeTextElement("td", sum10.toFixed(2));
194
 				output.writeTextElement("td", sum10.toFixed(2));
196
 				output.writeTextElement("td", sum4.toFixed(2));
195
 				output.writeTextElement("td", sum4.toFixed(2));

+ 30
- 30
config/Reports/inventory.xml View File

15
     </menu>
15
     </menu>
16
     <program>
16
     <program>
17
         <![CDATA[
17
         <![CDATA[
18
-            this.windowTitle = "Typica - Current Inventory and Availability Projection";
18
+            this.windowTitle = TTR("inventoryreport", "Typica - Current Inventory and Availability Projection");
19
             var report = findChildObject(this, 'report');
19
             var report = findChildObject(this, 'report');
20
             var printMenu = findChildObject(this, 'print');
20
             var printMenu = findChildObject(this, 'print');
21
             printMenu.triggered.connect(function() {
21
             printMenu.triggered.connect(function() {
22
                 report.print();
22
                 report.print();
23
             });
23
             });
24
             var sortBox = findChildObject(this, 'sort');
24
             var sortBox = findChildObject(this, 'sort');
25
-            sortBox.addItem("Coffee A-Z");
26
-            sortBox.addItem("Coffee Z-A");
27
-            sortBox.addItem("Stock Ascending");
28
-            sortBox.addItem("Stock Descending");
29
-            sortBox.addItem("Sacks Ascending");
30
-            sortBox.addItem("Sacks Descending");
31
-            sortBox.addItem("Unit Cost Ascending");
32
-            sortBox.addItem("Unit Cost Descending");
33
-            sortBox.addItem("Stock Cost Ascending");
34
-            sortBox.addItem("Stock Cost Descending");
35
-            sortBox.addItem("Use Rate Ascending");
36
-            sortBox.addItem("Use Rate Descending");
37
-            sortBox.addItem("Availability Shortest-Longest");
38
-            sortBox.addItem("Availability Longest-Shortest");
25
+            sortBox.addItem(TTR("inventoryreport", "Coffee A-Z"));
26
+            sortBox.addItem(TTR("inventoryreport", "Coffee Z-A"));
27
+            sortBox.addItem(TTR("inventoryreport", "Stock Ascending"));
28
+            sortBox.addItem(TTR("inventoryreport", "Stock Descending"));
29
+            sortBox.addItem(TTR("inventoryreport", "Sacks Ascending"));
30
+            sortBox.addItem(TTR("inventoryreport", "Sacks Descending"));
31
+            sortBox.addItem(TTR("inventoryreport", "Unit Cost Ascending"));
32
+            sortBox.addItem(TTR("inventoryreport", "Unit Cost Descending"));
33
+            sortBox.addItem(TTR("inventoryreport", "Stock Cost Ascending"));
34
+            sortBox.addItem(TTR("inventoryreport", "Stock Cost Descending"));
35
+            sortBox.addItem(TTR("inventoryreport", "Use Rate Ascending"));
36
+            sortBox.addItem(TTR("inventoryreport", "Use Rate Descending"));
37
+            sortBox.addItem(TTR("inventoryreport", "Availability Shortest-Longest"));
38
+            sortBox.addItem(TTR("inventoryreport", "Availability Longest-Shortest"));
39
             sortBox.currentIndex = QSettings.value("inventory_sort", 0);
39
             sortBox.currentIndex = QSettings.value("inventory_sort", 0);
40
 			var unitBox = findChildObject(this, 'unit');
40
 			var unitBox = findChildObject(this, 'unit');
41
-			unitBox.addItem("Kg");
42
-			unitBox.addItem("Lb");
41
+			unitBox.addItem(TTR("inventoryreport", "Kg"));
42
+			unitBox.addItem(TTR("inventoryreport", "Lb"));
43
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
43
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
44
             function refresh() {
44
             function refresh() {
45
                 var buffer = new QBuffer;
45
                 var buffer = new QBuffer;
50
                 output.writeStartElement("html");
50
                 output.writeStartElement("html");
51
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
51
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
52
                 output.writeStartElement("head");
52
                 output.writeStartElement("head");
53
-                output.writeTextElement("title", "Current Inventory and Availability Projection");
53
+                output.writeTextElement("title", TTR("inventoryreport", "Current Inventory and Availability Projection"));
54
                 output.writeEndElement();
54
                 output.writeEndElement();
55
                 output.writeStartElement("body");
55
                 output.writeStartElement("body");
56
-                output.writeTextElement("h1", "Current Inventory and Availability Projection");
57
-                output.writeTextElement("p", "This is a report showing how much of each coffee is available, the cost of that coffee, the daily rate of use for that coffee, and the date the coffee will be gone if use continues at the current rate.");
56
+                output.writeTextElement("h1", TTR("inventoryreport", "Current Inventory and Availability Projection"));
57
+                output.writeTextElement("p", TTR("inventoryreport", "This is a report showing how much of each coffee is available, the cost of that coffee, the daily rate of use for that coffee, and the date the coffee will be gone if use continues at the current rate."));
58
                 output.writeStartElement("table");
58
                 output.writeStartElement("table");
59
                 output.writeAttribute("rules", "groups");
59
                 output.writeAttribute("rules", "groups");
60
                 output.writeAttribute("cellpadding", "3px");
60
                 output.writeAttribute("cellpadding", "3px");
61
                 output.writeStartElement("thead");
61
                 output.writeStartElement("thead");
62
                 output.writeStartElement("tr");
62
                 output.writeStartElement("tr");
63
-                output.writeTextElement("th", "Coffee");
63
+                output.writeTextElement("th", TTR("inventoryreport", "Coffee"));
64
 				switch(unitBox.currentIndex)
64
 				switch(unitBox.currentIndex)
65
 				{
65
 				{
66
 					case 0:
66
 					case 0:
67
-						output.writeTextElement("th", "Stock (Kg)");
67
+						output.writeTextElement("th", TTR("inventoryreport", "Stock (Kg)"));
68
 						break;
68
 						break;
69
 					case 1:
69
 					case 1:
70
-						output.writeTextElement("th", "Stock (Lb)");
70
+						output.writeTextElement("th", TTR("inventoryreport", "Stock (Lb)"));
71
 						break;
71
 						break;
72
 				}
72
 				}
73
-                output.writeTextElement("th", "Sacks");
74
-                output.writeTextElement("th", "Unit Cost");
75
-                output.writeTextElement("th", "Stock Cost");
76
-                output.writeTextElement("th", "Use Rate");
77
-                output.writeTextElement("th", "Availability");
73
+                output.writeTextElement("th", TTR("inventoryreport", "Sacks"));
74
+                output.writeTextElement("th", TTR("inventoryreport", "Unit Cost"));
75
+                output.writeTextElement("th", TTR("inventoryreport", "Stock Cost"));
76
+                output.writeTextElement("th", TTR("inventoryreport", "Use Rate"));
77
+                output.writeTextElement("th", TTR("inventoryreport", "Availability"));
78
                 output.writeEndElement();
78
                 output.writeEndElement();
79
                 output.writeEndElement();
79
                 output.writeEndElement();
80
                 output.writeStartElement("tbody");
80
                 output.writeStartElement("tbody");
155
                 output.writeEndElement();
155
                 output.writeEndElement();
156
                 output.writeStartElement("tfoot");
156
                 output.writeStartElement("tfoot");
157
                 output.writeStartElement("tr");
157
                 output.writeStartElement("tr");
158
-                output.writeTextElement("th", "Totals");
158
+                output.writeTextElement("th", TTR("inventoryreport", "Totals"));
159
                 query.prepare("SELECT (sum(quantity) / :conversion)::numeric(12,2), sum(quantity * (SELECT cost FROM purchase WHERE item = id))::numeric(12,2) FROM items WHERE quantity > 0");
159
                 query.prepare("SELECT (sum(quantity) / :conversion)::numeric(12,2), sum(quantity * (SELECT cost FROM purchase WHERE item = id))::numeric(12,2) FROM items WHERE quantity > 0");
160
 				switch(unitBox.currentIndex)
160
 				switch(unitBox.currentIndex)
161
 				{
161
 				{
175
                 output.writeTextElement("td", "");
175
                 output.writeTextElement("td", "");
176
                 output.writeTextElement("td", "");
176
                 output.writeTextElement("td", "");
177
                 query = query.invalidate();
177
                 query = query.invalidate();
178
-				output.writeEndElement();
178
+                output.writeEndElement();
179
                 output.writeEndElement();
179
                 output.writeEndElement();
180
                 output.writeEndElement();
180
                 output.writeEndElement();
181
                 output.writeEndElement();
181
                 output.writeEndElement();

+ 156
- 0
config/Reports/invoices.xml View File

1
+<window id="invoicereport">
2
+    <reporttitle>Purchase:->Invoices</reporttitle>
3
+    <layout type="vertical">
4
+        <layout type="horizontal">
5
+            <daterange id="dates" initial="23" /><!-- Lifetime -->
6
+            <label>Vendor: </label>
7
+            <sqldrop id="vendor" />
8
+            <label>Search: </label>
9
+            <line id="search" />
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
+    <program>
18
+        <![CDATA[
19
+            var vendor = findChildObject(this, "vendor");
20
+            vendor.addItem(TTR("invoicereport", "Any"));
21
+            var query = new QSqlQuery();
22
+            query.exec("SELECT DISTINCT vendor FROM invoices");
23
+            while(query.next()) {
24
+                vendor.addItem(query.value(0));
25
+            }
26
+            vendor['currentIndexChanged(int)'].connect(refresh);
27
+            var dateSelect = findChildObject(this, 'dates');
28
+            query.exec("SELECT time::date FROM invoices WHERE time = (SELECT min(time) FROM invoices) OR time = (SELECT max(time) FROM invoices) ORDER BY time ASC");
29
+            query.next();
30
+            var lifetimeStartDate = query.value(0);
31
+            var lifetimeEndDate;
32
+            if(query.next()) {
33
+                lifetimeEndDate = query.value(0);
34
+            } else {
35
+                lifetimeEndDate = lifetimeStartDate;
36
+            }
37
+            dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
38
+            dateSelect.rangeUpdated.connect(refresh);
39
+            query = query.invalidate();
40
+            var search = findChildObject(this, "search");
41
+            search.editingFinished.connect(refresh);
42
+            var view = findChildObject(this, "report");
43
+            view.scriptLinkClicked.connect(function(url) {
44
+                var info = createWindow("invoiceinfo");
45
+                info.setInvoiceID(url);
46
+                var invquery = new QSqlQuery();
47
+                invquery.exec("SELECT time, invoice, vendor FROM invoices WHERE id = " + url);
48
+                invquery.next();
49
+                var timefield = findChildObject(info, 'date');
50
+                timefield.text = invquery.value(0);
51
+                var vendorfield = findChildObject(info, 'vendor');
52
+                vendorfield.text = invquery.value(2);
53
+                var invoicefield = findChildObject(info, 'invoice');
54
+                invoicefield.text = invquery.value(1);
55
+                var itemtable = findChildObject(info, 'itemtable');
56
+                itemtable.setQuery("SELECT record_type, item_id, description, (SELECT reference FROM items WHERE id = item_id) AS reference, (SELECT cost FROM purchase WHERE item = item_id) AS unit_cost, (SELECT quantity FROM purchase WHERE item = item_id) AS quantity, ((SELECT quantity FROM purchase WHERE item = item_id)/(SELECT conversion FROM lb_bag_conversion WHERE item = item_id))::numeric(12,2) AS sacks, cost FROM invoice_items WHERE invoice_id = " + url + " AND record_type = 'PURCHASE' UNION SELECT record_type, NULL, description, NULL, NULL, NULL, NULL, cost FROM invoice_items WHERE invoice_id = " + url + " AND record_type = 'FEE' ORDER BY item_id");
57
+                invquery = invquery.invalidate();
58
+            });
59
+            function refresh() {
60
+                var dateRange = dateSelect.currentRange();
61
+                var startDate = dateRange[0];
62
+                var endDate = dateRange[dateRange.length - 1];
63
+                var buffer = new QBuffer;
64
+                buffer.open(3);
65
+                var output = new XmlWriter(buffer);
66
+                output.writeStartDocument("1.0");
67
+                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">');
68
+                output.writeStartElement("html");
69
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
70
+                output.writeStartElement("head");
71
+                output.writeTextElement("title", TTR("invoicereport", "Invoices"));
72
+                output.writeEndElement();
73
+                output.writeStartElement("body");
74
+                output.writeTextElement("h1", TTR("invoicereport", "Invoices ") + startDate + " - " + endDate);
75
+                output.writeStartElement("table");
76
+                output.writeAttribute("style", "page-break-after: auto; text-align: left");
77
+                output.writeAttribute("rules", "groups");
78
+                output.writeAttribute("cellpadding", "3px");
79
+                output.writeStartElement("thead");
80
+                output.writeStartElement("tr");
81
+                output.writeTextElement("th", TTR("invoicereport", "Date"));
82
+                output.writeTextElement("th", TTR("invoicereport", "Vendor"));
83
+                output.writeTextElement("th", TTR("invoicereport", "Invoice"));
84
+                output.writeTextElement("th", TTR("invoicereport", "Cost"));
85
+                output.writeEndElement();
86
+                output.writeEndElement();
87
+                output.writeStartElement("tbody");
88
+                var query = new QSqlQuery();
89
+                var vendorclause = "";
90
+                if(vendor.currentIndex > 0) {
91
+                    vendorclause = " AND vendor = :vendor";
92
+                }
93
+                var searchclause = "";
94
+                if(search.text.length > 0)
95
+                {
96
+                    searchclause = " AND id IN (SELECT invoice_id FROM invoice_items WHERE item_id IN (SELECT item FROM certifications WHERE certification ~* :p1 UNION SELECT id FROM coffees WHERE origin ~* :p2 UNION SELECT id FROM items WHERE name ~* :p3 UNION SELECT id FROM coffees WHERE reference ~* :p4 UNION SELECT id FROM coffees WHERE region ~* :p5 UNION SELECT id FROM coffees WHERE producer ~* :p6 UNION SELECT id FROM coffees WHERE grade ~* :p7 UNION SELECT id FROM coffees WHERE milling ~* :p8 UNION SELECT id FROM coffees WHERE drying ~* :p9 UNION SELECT id FROM decaf_coffees WHERE decaf_method ~* :p10) OR description ~* :p11 UNION SELECT id FROM invoices WHERE invoice ~* :p12 UNION SELECT id FROM invoices WHERE vendor ~* :p13)";
97
+                }
98
+                query.prepare("SELECT id, time::date, vendor, invoice, (SELECT sum(cost) FROM invoice_items WHERE invoice_id = id)::numeric(12,2) AS cost FROM invoices WHERE time >= :sd AND time < :ed::date + interval '1 day'" + vendorclause + searchclause + " ORDER BY time DESC");
99
+                query.bind(":sd", startDate);
100
+                query.bind(":ed", endDate);
101
+                if(vendorclause.length > 0) {
102
+                    query.bind(":vendor", vendor.currentText);
103
+                }
104
+                if(searchclause.length > 0)
105
+                {
106
+                    var pattern = ".*" + search.text + ".*";
107
+                    query.bind(":p1", pattern);
108
+                    query.bind(":p2", pattern);
109
+                    query.bind(":p3", pattern);
110
+                    query.bind(":p4", pattern);
111
+                    query.bind(":p5", pattern);
112
+                    query.bind(":p6", pattern);
113
+                    query.bind(":p7", pattern);
114
+                    query.bind(":p8", pattern);
115
+                    query.bind(":p9", pattern);
116
+                    query.bind(":p10", pattern);
117
+                    query.bind(":p11", pattern);
118
+                    query.bind(":p12", pattern);
119
+                    query.bind(":p13", pattern);
120
+                }
121
+                query.exec();
122
+                var cost_sum = 0;
123
+                while(query.next()) {
124
+                    output.writeStartElement("tr");
125
+                    output.writeStartElement("td");
126
+                    output.writeStartElement("a");
127
+                    output.writeAttribute("href", "typica://script/" + query.value(0));
128
+                    output.writeCharacters(query.value(1));
129
+                    output.writeEndElement();
130
+                    output.writeEndElement();
131
+                    for(var i = 2; i <= 4; i++) {
132
+                        output.writeTextElement("td", query.value(i));
133
+                    }
134
+                    output.writeEndElement();
135
+                    cost_sum += Number(query.value(4));
136
+                }
137
+                query = query.invalidate();
138
+                output.writeEndElement();
139
+                output.writeStartElement("tfoot");
140
+                output.writeStartElement("tr");
141
+                output.writeEmptyElement("td");
142
+                output.writeEmptyElement("td");
143
+                output.writeTextElement("th", TTR("invoicereport", "Total:"));
144
+                output.writeTextElement("td", Number(cost_sum).toFixed(2));
145
+                output.writeEndElement();
146
+                output.writeEndElement();
147
+                output.writeEndElement();
148
+                output.writeEndElement();
149
+                output.writeEndElement();
150
+                output.writeEndDocument();
151
+                view.setContent(buffer);
152
+                buffer.close();
153
+            }
154
+            refresh();
155
+        ]]>
156
+    </program>

+ 42
- 36
config/Reports/itemtransactions.xml View File

18
 	</menu>
18
 	</menu>
19
 	<program>
19
 	<program>
20
 		<![CDATA[
20
 		<![CDATA[
21
-			this.windowTitle = "Typica - Item Transactions";
21
+			this.windowTitle = TTR("item_transactions", "Typica - Item Transactions");
22
 			var itemBox = findChildObject(this, 'item');
22
 			var itemBox = findChildObject(this, 'item');
23
 			var unitBox = findChildObject(this, 'unit');
23
 			var unitBox = findChildObject(this, 'unit');
24
-			unitBox.addItem("Kg");
25
-			unitBox.addItem("Lb");
24
+			unitBox.addItem(TTR("item_transactions", "Kg"));
25
+			unitBox.addItem(TTR("item_transactions", "Lb"));
26
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
26
 			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
27
 			unitBox['currentIndexChanged(int)'].connect(function() {
27
 			unitBox['currentIndexChanged(int)'].connect(function() {
28
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
28
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
45
 				output.writeStartElement("html");
45
 				output.writeStartElement("html");
46
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
46
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
47
 				output.writeStartElement("head");
47
 				output.writeStartElement("head");
48
-				output.writeTextElement("title", "Item Transactions");
48
+				output.writeTextElement("title", TTR("item_transactions", "Item Transactions"));
49
 				output.writeStartElement("script");
49
 				output.writeStartElement("script");
50
 				var scriptFile = new QFile(QSettings.value("config") + "/Scripts/d3.min.js");
50
 				var scriptFile = new QFile(QSettings.value("config") + "/Scripts/d3.min.js");
51
 				scriptFile.open(1);
51
 				scriptFile.open(1);
64
 				
64
 				
65
 				output.writeEndElement();
65
 				output.writeEndElement();
66
 				output.writeStartElement("body");
66
 				output.writeStartElement("body");
67
-				output.writeTextElement("h1", "Item Transactions:");
67
+				output.writeTextElement("h1", TTR("item_transactions", "Item Transactions:"));
68
 				output.writeStartElement("table");
68
 				output.writeStartElement("table");
69
 				output.writeStartElement("tr");
69
 				output.writeStartElement("tr");
70
 				output.writeStartElement("td");
70
 				output.writeStartElement("td");
77
 				query.exec();
77
 				query.exec();
78
 				if(query.next()) {
78
 				if(query.next()) {
79
 					output.writeStartElement("td");
79
 					output.writeStartElement("td");
80
-					output.writeTextElement("strong", "Reference: ");
80
+					output.writeTextElement("strong", TTR("item_transactions", "Reference: "));
81
 					output.writeTextElement("span", query.value(0));
81
 					output.writeTextElement("span", query.value(0));
82
 					output.writeEndElement(); // td
82
 					output.writeEndElement(); // td
83
 					output.writeStartElement("td");
83
 					output.writeStartElement("td");
84
-					output.writeTextElement("strong", "Category: ");
84
+					output.writeTextElement("strong", TTR("item_transactions", "Category: "));
85
 					output.writeTextElement("span", query.value(1));
85
 					output.writeTextElement("span", query.value(1));
86
 					output.writeEndElement(); //td
86
 					output.writeEndElement(); //td
87
 					output.writeEndElement(); //tr
87
 					output.writeEndElement(); //tr
91
 					if(query.next()) {
91
 					if(query.next()) {
92
 						output.writeStartElement("tr");
92
 						output.writeStartElement("tr");
93
 						output.writeStartElement("td");
93
 						output.writeStartElement("td");
94
-						output.writeTextElement("strong", "Origin: ");
94
+						output.writeTextElement("strong", TTR("item_transactions", "Origin: "));
95
 						output.writeTextElement("span", query.value(0));
95
 						output.writeTextElement("span", query.value(0));
96
 						output.writeEndElement(); // td
96
 						output.writeEndElement(); // td
97
 						output.writeStartElement("td");
97
 						output.writeStartElement("td");
98
-						output.writeTextElement("strong", "Region: ");
98
+						output.writeTextElement("strong", TTR("item_transactions", "Region: "));
99
 						output.writeTextElement("span", query.value(1));
99
 						output.writeTextElement("span", query.value(1));
100
 						output.writeEndElement(); // td
100
 						output.writeEndElement(); // td
101
 						output.writeStartElement("td");
101
 						output.writeStartElement("td");
102
-						output.writeTextElement("strong", "Producer: ");
102
+						output.writeTextElement("strong", TTR("item_transactions", "Producer: "));
103
 						output.writeTextElement("span", query.value(2));
103
 						output.writeTextElement("span", query.value(2));
104
 						output.writeEndElement(); // td
104
 						output.writeEndElement(); // td
105
 						output.writeEndElement(); // tr
105
 						output.writeEndElement(); // tr
106
 						output.writeStartElement("tr");
106
 						output.writeStartElement("tr");
107
 						output.writeStartElement("td");
107
 						output.writeStartElement("td");
108
-						output.writeTextElement("strong", "Grade: ");
108
+						output.writeTextElement("strong", TTR("item_transactions", "Grade: "));
109
 						output.writeTextElement("span", query.value(3));
109
 						output.writeTextElement("span", query.value(3));
110
 						output.writeEndElement(); // td
110
 						output.writeEndElement(); // td
111
 						output.writeStartElement("td");
111
 						output.writeStartElement("td");
112
-						output.writeTextElement("strong", "Milling: ");
112
+						output.writeTextElement("strong", TTR("item_transactions", "Milling: "));
113
 						output.writeTextElement("span", query.value(4));
113
 						output.writeTextElement("span", query.value(4));
114
 						output.writeEndElement(); // td
114
 						output.writeEndElement(); // td
115
 						output.writeStartElement("td");
115
 						output.writeStartElement("td");
116
-						output.writeTextElement("strong", "Drying: ");
116
+						output.writeTextElement("strong", TTR("item_transactions", "Drying: "));
117
 						output.writeTextElement("span", query.value(5));
117
 						output.writeTextElement("span", query.value(5));
118
 						output.writeEndElement(); // td
118
 						output.writeEndElement(); // td
119
 						output.writeEndElement(); // tr
119
 						output.writeEndElement(); // tr
124
 							output.writeStartElement("tr");
124
 							output.writeStartElement("tr");
125
 							output.writeStartElement("td");
125
 							output.writeStartElement("td");
126
 							output.writeAttribute("colspan", "3");
126
 							output.writeAttribute("colspan", "3");
127
-							output.writeTextElement("strong", "Decaffeination Method: ");
127
+							output.writeTextElement("strong", TTR("item_transactions", "Decaffeination Method: "));
128
 							output.writeTextElement("span", query.value(0));
128
 							output.writeTextElement("span", query.value(0));
129
 							output.writeEndElement(); // td
129
 							output.writeEndElement(); // td
130
 							output.writeEndElement(); // tr
130
 							output.writeEndElement(); // tr
176
 					scriptFile.close();
176
 					scriptFile.close();
177
 					output.writeEndElement();
177
 					output.writeEndElement();
178
 					
178
 					
179
-					query.prepare("SELECT time::date, type, quantity / :c1, balance / :c2, (SELECT files FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id)), (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE'), (SELECT vendor || ' ' || invoice FROM invoices WHERE id = (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE')), (SELECT name FROM items WHERE id = (SELECT roasted_id FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id))) FROM item_history(:item)");
179
+					query.prepare("SELECT time::date, type, quantity / :c1, balance / :c2, (SELECT files FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id)), (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE'), (SELECT vendor || ' ' || invoice FROM invoices WHERE id = (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE')), (SELECT name FROM items WHERE id = (SELECT roasted_id FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id))), customer, reason FROM item_history(:item)");
180
 					switch(unitBox.currentIndex)
180
 					switch(unitBox.currentIndex)
181
 					{
181
 					{
182
 						case 0:
182
 						case 0:
192
 					query.exec();
192
 					query.exec();
193
 					output.writeStartElement("table");
193
 					output.writeStartElement("table");
194
 					output.writeStartElement("tr");
194
 					output.writeStartElement("tr");
195
-					output.writeTextElement("th", "Date");
196
-					output.writeTextElement("th", "Type");
197
-					output.writeTextElement("th", "Quantity");
198
-					output.writeTextElement("th", "Balance");
199
-					output.writeTextElement("th", "Record");
195
+					output.writeTextElement("th", TTR("item_transactions", "Date"));
196
+					output.writeTextElement("th", TTR("item_transactions", "Type"));
197
+					output.writeTextElement("th", TTR("item_transactions", "Quantity"));
198
+					output.writeTextElement("th", TTR("item_transactions", "Balance"));
199
+					output.writeTextElement("th", TTR("item_transactions", "Record"));
200
 					output.writeEndElement(); // tr
200
 					output.writeEndElement(); // tr
201
 					var prev_balance = "0";
201
 					var prev_balance = "0";
202
 					var prev_prec = 0;
202
 					var prev_prec = 0;
203
 					var cur_prec = 0;
203
 					var cur_prec = 0;
204
+                                        var max_prec = 3;
204
 					while(query.next()) {
205
 					while(query.next()) {
205
 						output.writeStartElement("tr");
206
 						output.writeStartElement("tr");
206
 						output.writeAttribute("class", query.value(1));
207
 						output.writeAttribute("class", query.value(1));
207
 						output.writeTextElement("td", query.value(0));
208
 						output.writeTextElement("td", query.value(0));
208
 						output.writeTextElement("td", query.value(1));
209
 						output.writeTextElement("td", query.value(1));
210
+                                                var split = prev_balance.split('.');
211
+                                                if(split.length > 1) {
212
+                                                    prev_prec = split[1].length;
213
+                                                } else {
214
+                                                    prev_prec = 0;
215
+                                                }
216
+                                                split = query.value(2).split('.');
217
+                                                if(split.length > 1) {
218
+                                                    cur_prec = split[1].length;
219
+                                                } else {
220
+                                                    cur_prec = 0;
221
+                                                }
222
+                                                var prec = prev_prec > cur_prec ? prev_prec : cur_prec;
223
+                                                var prec = (prec > max_prec ? max_prec : prec);
209
 						if(query.value(1) == "INVENTORY") {
224
 						if(query.value(1) == "INVENTORY") {
210
-							var split = prev_balance.split('.');
211
-							if(split.length > 1) {
212
-								prev_prec = split[1].length;
213
-							} else {
214
-								prev_prec = 0;
215
-							}
216
-							split = query.value(2).split('.');
217
-							if(split.length > 1) {
218
-								cur_prec = split[1].length;
219
-							} else {
220
-								cur_prec = 0;
221
-							}
222
-							var prec = prev_prec > cur_prec ? prev_prec : cur_prec;
223
-							output.writeTextElement("td", (Number(query.value(2)) - Number(prev_balance)).toFixed(prec));
225
+                                                    output.writeTextElement("td", (Number(query.value(2)) - Number(prev_balance)).toFixed(prec));
224
 						} else {
226
 						} else {
225
-							output.writeTextElement("td", query.value(2));
227
+                                                    output.writeTextElement("td", (Number(query.value(2)).toFixed(prec)));
226
 						}
228
 						}
227
-						output.writeTextElement("td", query.value(3));
229
+						output.writeTextElement("td", (Number(query.value(3)).toFixed(prec)));
228
 						prev_balance = query.value(3);
230
 						prev_balance = query.value(3);
229
 						if(query.value(1) == "PURCHASE") {
231
 						if(query.value(1) == "PURCHASE") {
230
 							output.writeStartElement("td");
232
 							output.writeStartElement("td");
240
 							output.writeCDATA(query.value(7) + " " + query.value(4));
242
 							output.writeCDATA(query.value(7) + " " + query.value(4));
241
 							output.writeEndElement();
243
 							output.writeEndElement();
242
 							output.writeEndElement();
244
 							output.writeEndElement();
245
+                                                } else if(query.value(1) == "LOSS") {
246
+                                                    output.writeTextElement("td", query.value(9));
247
+                                                } else if(query.value(1) == "SALE") {
248
+                                                    output.writeTextElement("td", query.value(8));
243
 						} else {
249
 						} else {
244
 							output.writeTextElement("td", "");
250
 							output.writeTextElement("td", "");
245
 						}
251
 						}

+ 117
- 106
config/Reports/monthcompare.xml View File

13
     </menu>
13
     </menu>
14
     <program>
14
     <program>
15
         <![CDATA[
15
         <![CDATA[
16
-            this.windowTitle = "Typica - Previous Year Production Comparison By Month";
16
+            this.windowTitle = TTR("pytdprodcomp", "Typica - Previous Year Production Comparison By Month");
17
             var view = findChildObject(this, 'report');
17
             var view = findChildObject(this, 'report');
18
             var printMenu = findChildObject(this, 'print');
18
             var printMenu = findChildObject(this, 'print');
19
             printMenu.triggered.connect(function() {
19
             printMenu.triggered.connect(function() {
20
                 view.print();
20
                 view.print();
21
             });
21
             });
22
-			var unitBox = findChildObject(this, 'unit');
23
-			unitBox.addItem("Kg");
24
-			unitBox.addItem("Lb");
25
-			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
26
-			unitBox['currentIndexChanged(int)'].connect(function() {
27
-				QSettings.setValue("script/report_unit", unitBox.currentIndex);
28
-				refresh();
29
-			});
22
+            var unitBox = findChildObject(this, 'unit');
23
+            unitBox.addItem(TTR("pytdprodcomp", "Kg"));
24
+            unitBox.addItem(TTR("pytdprodcomp", "Lb"));
25
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
26
+            unitBox['currentIndexChanged(int)'].connect(function() {
27
+                QSettings.setValue("script/report_unit", unitBox.currentIndex);
28
+                refresh();
29
+            });
30
             function refresh() {
30
             function refresh() {
31
                 var buffer = new QBuffer;
31
                 var buffer = new QBuffer;
32
                 buffer.open(3);
32
                 buffer.open(3);
36
                 output.writeStartElement("html");
36
                 output.writeStartElement("html");
37
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
37
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
38
                 output.writeStartElement("head");
38
                 output.writeStartElement("head");
39
-                output.writeTextElement("title", "Previous Year Production Comparison By Month");
39
+                output.writeTextElement("title", TTR("pytdprodcomp", "Previous Year Production Comparison By Month"));
40
                 output.writeEndElement();
40
                 output.writeEndElement();
41
                 output.writeStartElement("body");
41
                 output.writeStartElement("body");
42
-                output.writeTextElement("h1", "Previous Year Production Comparison By Month");
43
-				switch(unitBox.currentIndex)
44
-				{
45
-					case 0:
46
-						output.writeTextElement("p", "This report compares total roasted coffee production in kilograms with the previous year on a monthly basis.");
47
-						break;
48
-					case 1:
49
-						output.writeTextElement("p", "This report compares total roasted coffee production in pounds with the previous year on a monthly basis.");
50
-						break;
51
-				}
42
+                output.writeTextElement("h1", TTR("pytdprodcomp", "Previous Year Production Comparison By Month"));
43
+                switch(unitBox.currentIndex)
44
+                {
45
+                    case 0:
46
+                        output.writeTextElement("p", TTR("pytdprodcomp", "This report compares total roasted coffee production in kilograms with the previous year on a monthly basis."));
47
+                        break;
48
+                    case 1:
49
+                        output.writeTextElement("p", TTR("pytdprodcomp", "This report compares total roasted coffee production in pounds with the previous year on a monthly basis."));
50
+                        break;
51
+                }
52
                 output.writeStartElement("table");
52
                 output.writeStartElement("table");
53
                 output.writeAttribute("style", "page-break-after:auto;");
53
                 output.writeAttribute("style", "page-break-after:auto;");
54
                 output.writeAttribute("rules", "groups");
54
                 output.writeAttribute("rules", "groups");
55
                 output.writeAttribute("cellpadding", "3px");
55
                 output.writeAttribute("cellpadding", "3px");
56
                 output.writeStartElement("thead");
56
                 output.writeStartElement("thead");
57
                 output.writeStartElement("tr");
57
                 output.writeStartElement("tr");
58
-                output.writeTextElement("th", "Month");
59
-				var query = new QSqlQuery();
60
-				query.exec("SELECT EXTRACT(YEAR FROM 'now'::date) AS current_year");
61
-				query.next();
62
-				var current_year = query.value(0);
58
+                output.writeTextElement("th", TTR("pytdprodcomp", "Month"));
59
+                var query = new QSqlQuery();
60
+                query.exec("SELECT EXTRACT(YEAR FROM 'now'::date) AS current_year");
61
+                query.next();
62
+                var current_year = query.value(0);
63
                 output.writeTextElement("th", current_year-1);
63
                 output.writeTextElement("th", current_year-1);
64
                 output.writeTextElement("th", current_year);
64
                 output.writeTextElement("th", current_year);
65
                 output.writeTextElement("th", "Change");
65
                 output.writeTextElement("th", "Change");
66
                 output.writeEndElement();
66
                 output.writeEndElement();
67
                 output.writeEndElement();
67
                 output.writeEndElement();
68
                 output.writeStartElement("tbody");
68
                 output.writeStartElement("tbody");
69
-				var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
70
-				var current_data = new Array();
71
-				var previous_data = new Array();
72
-				for(var i = 0; i < 12; i++) {
73
-					var q = "SELECT SUM(roasted_quantity) FROM roasting_log WHERE roasted_id IS NOT NULL AND time > '"
74
-					q += current_year;
75
-					q += "-";
76
-					q += 1+i;
77
-					q += "-01' AND time < '";
78
-					if(i == 11) {
79
-						q += 1+current_year;
80
-					} else {
81
-						q += current_year;
82
-					}
83
-					q += "-";
84
-					if(i == 11) {
85
-						q += "01"
86
-					} else {
87
-						q += 2+i;
88
-					}
89
-					q += "-01'";
90
-					query.exec(q);
91
-					query.next();
92
-					current_data.push(query.value(0));
93
-				}
94
-				for(var i = 0; i < 12; i++) {
95
-					var q = "SELECT SUM(roasted_quantity) FROM roasting_log WHERE roasted_id IS NOT NULL AND time > '"
96
-					q += current_year-1;
97
-					q += "-";
98
-					q += 1+i;
99
-					q += "-01' AND time < '";
100
-					if(i == 11) {
101
-						q += current_year;
102
-					} else {
103
-						q += current_year-1;
104
-					}
105
-					q += "-";
106
-					if(i == 11) {
107
-						q += "01"
108
-					} else {
109
-						q += 2+i;
110
-					}
111
-					q += "-01'";
112
-					query.exec(q);
113
-					query.next();
114
-					previous_data.push(query.value(0));
115
-				}
116
-				query = query.invalidate();
117
-				for(var i = 0; i < 12; i++) {
118
-					output.writeStartElement("tr");
119
-					output.writeTextElement("td", month_names[i]);
120
-					switch(unitBox.currentIndex)
121
-					{
122
-						case 0:
123
-							output.writeTextElement("td", Number(previous_data[i] / 2.2).toFixed(2));
124
-							output.writeTextElement("td", Number(current_data[i] / 2.2).toFixed(2));
125
-							output.writeTextElement("td", Number((current_data[i] - previous_data[i]) / 2.2).toFixed(2));
126
-							break;
127
-						case 1:
128
-							output.writeTextElement("td", Number(previous_data[i]).toFixed(2));
129
-							output.writeTextElement("td", Number(current_data[i]).toFixed(2));
130
-							output.writeTextElement("td", Number(current_data[i]-previous_data[i]).toFixed(2));
131
-							break;
132
-					}
133
-					output.writeEndElement();
134
-				}
69
+                var month_names = [TTR("pytdprodcomp", "January"),
70
+                    TTR("pytdprodcomp", "February"),
71
+                    TTR("pytdprodcomp", "March"),
72
+                    TTR("pytdprodcomp", "April"),
73
+                    TTR("pytdprodcomp", "May"),
74
+                    TTR("pytdprodcomp", "June"),
75
+                    TTR("pytdprodcomp", "July"),
76
+                    TTR("pytdprodcomp", "August"),
77
+                    TTR("pytdprodcomp", "September"),
78
+                    TTR("pytdprodcomp", "October"),
79
+                    TTR("pytdprodcomp", "November"),
80
+                    TTR("pytdprodcomp", "December")];
81
+                var current_data = new Array();
82
+                var previous_data = new Array();
83
+                for(var i = 0; i < 12; i++) {
84
+                    var q = "SELECT SUM(roasted_quantity) FROM roasting_log WHERE roasted_id IS NOT NULL AND time > '"
85
+                    q += current_year;
86
+                    q += "-";
87
+                    q += 1+i;
88
+                    q += "-01' AND time < '";
89
+                    if(i == 11) {
90
+                        q += 1+current_year;
91
+                    } else {
92
+                        q += current_year;
93
+                    }
94
+                    q += "-";
95
+                    if(i == 11) {
96
+                        q += "01"
97
+                    } else {
98
+                        q += 2+i;
99
+                    }
100
+                    q += "-01'";
101
+                    query.exec(q);
102
+                    query.next();
103
+                    current_data.push(query.value(0));
104
+                }
105
+                for(var i = 0; i < 12; i++) {
106
+                    var q = "SELECT SUM(roasted_quantity) FROM roasting_log WHERE roasted_id IS NOT NULL AND time > '"
107
+                    q += current_year-1;
108
+                    q += "-";
109
+                    q += 1+i;
110
+                    q += "-01' AND time < '";
111
+                    if(i == 11) {
112
+                        q += current_year;
113
+                    } else {
114
+                        q += current_year-1;
115
+                    }
116
+                    q += "-";
117
+                    if(i == 11) {
118
+                        q += "01"
119
+                    } else {
120
+                        q += 2+i;
121
+                    }
122
+                    q += "-01'";
123
+                    query.exec(q);
124
+                    query.next();
125
+                    previous_data.push(query.value(0));
126
+                }
127
+                query = query.invalidate();
128
+                for(var i = 0; i < 12; i++) {
129
+                    output.writeStartElement("tr");
130
+                    output.writeTextElement("td", month_names[i]);
131
+                    switch(unitBox.currentIndex)
132
+                    {
133
+                        case 0:
134
+                            output.writeTextElement("td", Number(previous_data[i] / 2.2).toFixed(2));
135
+                            output.writeTextElement("td", Number(current_data[i] / 2.2).toFixed(2));
136
+                            output.writeTextElement("td", Number((current_data[i] - previous_data[i]) / 2.2).toFixed(2));
137
+                            break;
138
+                        case 1:
139
+                            output.writeTextElement("td", Number(previous_data[i]).toFixed(2));
140
+                            output.writeTextElement("td", Number(current_data[i]).toFixed(2));
141
+                            output.writeTextElement("td", Number(current_data[i]-previous_data[i]).toFixed(2));
142
+                            break;
143
+                    }
144
+                    output.writeEndElement();
145
+                }
135
                 output.writeEndElement();
146
                 output.writeEndElement();
136
                 output.writeStartElement("tfoot");
147
                 output.writeStartElement("tfoot");
137
-                output.writeTextElement("th", "Totals");
138
-				var current_total = current_data.reduce(function(a,b) {return Number(a) + Number(b);}, 0);
139
-				var previous_total = previous_data.reduce(function(a,b) {return Number(a) + Number(b);}, 0);
140
-				switch(unitBox.currentIndex)
141
-				{
142
-					case 0:
143
-						output.writeTextElement("td", (previous_total/2.2).toFixed(2));
148
+                output.writeTextElement("th", TTR("pytdprodcomp", "Totals"));
149
+                var current_total = current_data.reduce(function(a,b) {return Number(a) + Number(b);}, 0);
150
+                var previous_total = previous_data.reduce(function(a,b) {return Number(a) + Number(b);}, 0);
151
+                switch(unitBox.currentIndex)
152
+                {
153
+                    case 0:
154
+                        output.writeTextElement("td", (previous_total/2.2).toFixed(2));
144
 						output.writeTextElement("td", (current_total/2.2).toFixed(2));
155
 						output.writeTextElement("td", (current_total/2.2).toFixed(2));
145
-						output.writeTextElement("td", ((current_total-previous_total)/2.2).toFixed(2));
146
-						break;
147
-					case 1:
148
-						output.writeTextElement("td", previous_total.toFixed(2));
149
-						output.writeTextElement("td", current_total.toFixed(2));
150
-						output.writeTextElement("td", (current_total-previous_total).toFixed(2));
151
-						break;
152
-				}
156
+                        output.writeTextElement("td", ((current_total-previous_total)/2.2).toFixed(2));
157
+                        break;
158
+                    case 1:
159
+                        output.writeTextElement("td", previous_total.toFixed(2));
160
+                        output.writeTextElement("td", current_total.toFixed(2));
161
+                        output.writeTextElement("td", (current_total-previous_total).toFixed(2));
162
+                        break;
163
+                }
153
                 output.writeEndElement();
164
                 output.writeEndElement();
154
                 output.writeEndElement();
165
                 output.writeEndElement();
155
                 output.writeEndElement();
166
                 output.writeEndElement();

+ 186
- 0
config/Reports/productionsummary.xml View File

1
+<window id="dailyproduction">
2
+    <reporttitle>Production:->Production Summary</reporttitle>
3
+    <layout type="vertical">
4
+        <layout type="horizontal">
5
+            <label>Batch Type: </label>
6
+            <sqldrop id="batchtype" />
7
+            <label>Approval: </label>
8
+            <sqldrop id="approval" />
9
+            <daterange id="dates" initial="9" /><!-- Current Month to Date-->
10
+            <label>Weight Unit:</label>
11
+            <sqldrop id="unit" />
12
+            <stretch />
13
+        </layout>
14
+        <webview id="report" />
15
+    </layout>
16
+    <menu name="File">
17
+        <item id="print" shortcut="Ctrl+P">Print</item>
18
+    </menu>
19
+    <program>
20
+        <![CDATA[
21
+            this.windowTitle = TTR("dailyproduction", "Typica - Production Summary");
22
+            var dateSelect = findChildObject(this, 'dates');
23
+            var dateQuery = new QSqlQuery();
24
+            dateQuery.exec("SELECT time::date FROM roasting_log WHERE time = (SELECT min(time) FROM roasting_log) OR time = (SELECT max(time) FROM roasting_log) ORDER BY time ASC");
25
+            dateQuery.next();
26
+            var lifetimeStartDate = dateQuery.value(0);
27
+            var lifetimeEndDate;
28
+            if(dateQuery.next()) {
29
+                lifetimeEndDate = dateQuery.value(0);
30
+            } else {
31
+                lifetimeEndDate = lifetimeStartDate;
32
+            }
33
+            dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
34
+            dateQuery = dateQuery.invalidate();
35
+            var unitBox = findChildObject(this, 'unit');
36
+            unitBox.addItem(TTR("dailyproduction", "Kg"));
37
+            unitBox.addItem(TTR("dailyproduction", "Lb"));
38
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
39
+            unitBox['currentIndexChanged(int)'].connect(function() {
40
+                QSettings.setValue("script/report_unit", unitBox.currentIndex);
41
+                refresh();
42
+            });
43
+            var batchType = findChildObject(this, 'batchtype');
44
+            batchType.addItem(TTR("dailyproduction", "Any"));
45
+            batchType.addItem(TTR("dailyproduction", "Production Roasts"));
46
+            batchType.addItem(TTR("dailyproduction", "Sample Roasts"));
47
+            batchType.currentIndex = QSettings.value("script/batchtypefilter", 1);
48
+            batchType['currentIndexChanged(int)'].connect(function() {
49
+                QSettings.setValue("script/batchtypefilter", batchType.currentIndex);
50
+                refresh();
51
+            });
52
+            var approval = findChildObject(this, 'approval');
53
+            approval.addItem(TTR("dailyproduction", "Any"));
54
+            approval.addItem(TTR("dailyproduction", "Approved"));
55
+            approval.addItem(TTR("dailyproduction", "Not Approved"));
56
+            approval.currentIndex = QSettings.value("script/approvalfilter", 1);
57
+            approval['currentIndexChanged(int)'].connect(function() {
58
+                QSettings.setValue("script/approvalfilter", approval.currentIndex);
59
+                refresh();
60
+            });
61
+            var view = findChildObject(this, 'report');
62
+            var printMenu = findChildObject(this, 'print');
63
+            printMenu.triggered.connect(function() {
64
+                view.print();
65
+            });
66
+            function refresh() {
67
+                var buffer = new QBuffer;
68
+                buffer.open(3);
69
+                var output = new XmlWriter(buffer);
70
+                output.writeStartDocument("1.0");
71
+                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">');
72
+                output.writeStartElement("html");
73
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
74
+                output.writeStartElement("head");
75
+                output.writeTextElement("title", TTR("dailyproduction", "Production Summary"));
76
+                output.writeEndElement();
77
+                output.writeStartElement("body");
78
+                var dateRange = dateSelect.currentRange();
79
+                var startDate = dateRange[0];
80
+                var endDate = dateRange[dateRange.length - 1];
81
+                output.writeTextElement("h1", TTR("dailyproduction", "Production Summary: ") + startDate + " - " + endDate);
82
+                var conversion = 1;
83
+                var unitText = TTR("dailyproduction", "Lb");
84
+                if(unitBox.currentIndex == 0) {
85
+                    conversion = 2.2;
86
+                    unitText = TTR("dailyproduction", "Kg");
87
+                }
88
+                var transaction_filter;
89
+                var approval_filter;
90
+                switch(batchType.currentIndex) {
91
+                    case 0:
92
+                        transaction_filter = "";
93
+                        break;
94
+                    case 1:
95
+                        transaction_filter = " AND transaction_type = 'ROAST'";
96
+                        break;
97
+                    case 2:
98
+                        transaction_filter = " AND transaction_type = 'SAMPLEROAST'";
99
+                        break;
100
+                }
101
+                switch(approval.currentIndex) {
102
+                    case 0:
103
+                        approval_filter = "";
104
+                        break;
105
+                    case 1:
106
+                        approval_filter = " AND approval = true";
107
+                        break;
108
+                    case 2:
109
+                        approval_filter = " AND approval = false";
110
+                        break;
111
+                }
112
+                var query = new QSqlQuery();
113
+                query.prepare("SELECT count(1), sum(unroasted_total_quantity) / :c1, sum(roasted_quantity) / :c2 FROM roasting_log WHERE time >= :sd AND time < :ed ::date + interval '1 day'" + transaction_filter + approval_filter);
114
+                query.bind(":c1", conversion);
115
+                query.bind(":c2", conversion);
116
+                query.bind(":sd", startDate);
117
+                query.bind(":ed", endDate);
118
+                query.exec();
119
+                query.next();
120
+                var batchesRoasted = query.value(0);
121
+                var unroastedSum = query.value(1);
122
+                var roastedSum = query.value(2);
123
+                output.writeTextElement("p", "" + roastedSum + " " + unitText + TTR("dailyproduction", " roasted from ") +
124
+                unroastedSum + " " + unitText + TTR("dailyproduction", " green in ") +
125
+                batchesRoasted + TTR("dailyproduction", " batches."));
126
+                query.prepare("SELECT time::date AS date, count(1), sum(unroasted_total_quantity) / :c1, sum(roasted_quantity) / :c2 FROM roasting_log WHERE time >= :sd AND time < :ed ::date + interval '1 day'" + transaction_filter + approval_filter + " GROUP BY date ORDER BY date ASC");
127
+                query.bind(":c1", conversion);
128
+                query.bind(":c2", conversion);
129
+                query.bind(":sd", startDate);
130
+                query.bind(":ed", endDate);
131
+                query.exec();
132
+                output.writeStartElement("table");
133
+                output.writeAttribute("rules", "groups");
134
+                output.writeAttribute("cellpadding", "3px");
135
+                output.writeStartElement("thead");
136
+                output.writeStartElement("tr");
137
+                output.writeTextElement("th", TTR("dailyproduction", "Date"));
138
+                output.writeTextElement("th", TTR("dailyproduction", "Batches"));
139
+                output.writeTextElement("th", TTR("dailyproduction", "Unroasted (") + unitText + ")");
140
+                output.writeTextElement("th", TTR("dailyproduction", "Roasted (") + unitText + ")");
141
+                output.writeEndElement();
142
+                output.writeEndElement();
143
+                output.writeStartElement("tbody");
144
+                while(query.next()) {
145
+                    output.writeStartElement("tr");
146
+                    output.writeStartElement("td");
147
+                    output.writeStartElement("a");
148
+                    output.writeAttribute("href", "typica://script/d" + query.value(0));
149
+                    output.writeCDATA(query.value(0));
150
+                    output.writeEndElement();
151
+                    output.writeEndElement();
152
+                    output.writeTextElement("td", query.value(1));
153
+                    output.writeTextElement("td", query.value(2));
154
+                    output.writeTextElement("td", query.value(3));
155
+                    output.writeEndElement();
156
+                }
157
+                output.writeEndElement();
158
+                output.writeStartElement("tfoot");
159
+                output.writeStartElement("tr");
160
+                output.writeStartElement("td");
161
+                output.writeTextElement("strong", TTR("dailyproduction", "Totals:"));
162
+                output.writeEndElement();
163
+                output.writeTextElement("td", batchesRoasted);
164
+                output.writeTextElement("td", unroastedSum);
165
+                output.writeTextElement("td", roastedSum);
166
+                output.writeEndElement();
167
+                output.writeEndElement();
168
+                output.writeEndElement();
169
+                output.writeEndElement();
170
+                output.writeEndElement();
171
+                output.writeEndDocument();
172
+                view.setContent(buffer);
173
+                buffer.close();
174
+                query = query.invalidate();
175
+            }
176
+            refresh();
177
+            dateSelect.rangeUpdated.connect(refresh);
178
+            view.scriptLinkClicked.connect(function(url) {
179
+                var arg = url.slice(1, url.length).split("-");
180
+                var details = createReport("dailyproductiondetail.xml");
181
+                var selector = findChildObject(details, "reportdate");
182
+                selector.setDate(arg[0], arg[1], arg[2]);
183
+            });
184
+        ]]>
185
+    </program>
186
+</window>

+ 186
- 0
config/Reports/reminders.xml View File

1
+<window id="remindersreport">
2
+	<reporttitle>Production:->Reminders</reporttitle>
3
+    <layout type="vertical">
4
+        <webview id="report" />
5
+    </layout>
6
+    <menu name="File">
7
+        <item id="print" shortcut="Ctrl+P">Print</item>
8
+    </menu>
9
+    <program>
10
+        <![CDATA[
11
+            var window = this;
12
+            this.windowTitle = TTR("remindersreport", "Typica - Reminders");
13
+            var report = findChildObject(this, 'report');
14
+            var printMenu = findChildObject(this, 'print');
15
+            printMenu.triggered.connect(function() {
16
+                report.print();
17
+            });
18
+            var e = new Array();
19
+            function populateReminderData() {
20
+                e = new Array();
21
+                var query = new QSqlQuery;
22
+                query.exec("SELECT id, reminder FROM reminders");
23
+                while(query.next())
24
+                {
25
+                    var reminder = JSON.parse(query.value(1));
26
+                    reminder.dbid = query.value(0);
27
+                    var start_time = "" + reminder.start_year + "-" + reminder.start_month + "-" + reminder.start_day + " " + reminder.start_time;
28
+                    if(reminder.condition == "PRODUCTIONWEIGHT")
29
+                    {
30
+                        var convert = 1;
31
+                        var unittext = TTR("remindersreport", " Lb");
32
+                        if(reminder.unit == "KG")
33
+                        {
34
+                            convert = 2.2;
35
+                            unittext = TTR("remindersreport", " Kg");
36
+                        }
37
+                        var dq = new QSqlQuery;
38
+                        dq.prepare("SELECT sum(roasted_quantity)/:conversion FROM roasting_log WHERE time > :since");
39
+                        dq.bind(":conversion", convert);
40
+                        dq.bind(":since", start_time);
41
+                        dq.exec();
42
+                        dq.next();
43
+                        var proportion;
44
+                        var remain;
45
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
46
+                        {
47
+                            proportion = 1;
48
+                        }
49
+                        else
50
+                        {
51
+                            proportion = Number(dq.value(0)) / reminder.value;
52
+                        }
53
+                        remain = (reminder.value - Number(dq.value(0))).toFixed(0);
54
+                        reminder.completion = proportion;
55
+                        reminder.detail = remain + unittext;
56
+                        dq = dq.invalidate();
57
+                    }
58
+                    else if(reminder.condition == "DAYS")
59
+                    {
60
+                        var dq = new QSqlQuery;
61
+                        dq.prepare("SELECT 'now'::date - :since::date");
62
+                        dq.bind(":since", start_time);
63
+                        dq.exec();
64
+                        dq.next();
65
+                        var proportion;
66
+                        var remain;
67
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
68
+                        {
69
+                            proportion = 1;
70
+                        }
71
+                        else
72
+                        {
73
+                            proportion = Number(dq.value(0)) / reminder.value;
74
+                        }
75
+                        remain = reminder.value - Number(dq.value(0));
76
+                        reminder.completion = proportion;
77
+                        reminder.detail = remain + TTR("remindersreport", " Days");
78
+                        dq = dq.invalidate();
79
+                    }
80
+                    else if(reminder.condition == "PRODUCTIONBATCHES")
81
+                    {
82
+                        var dq = new QSqlQuery;
83
+                        dq.prepare("SELECT count(1) FROM roasting_log WHERE time > :since");
84
+                        dq.bind(":since", start_time);
85
+                        dq.exec();
86
+                        dq.next();
87
+                        var proportion;
88
+                        var remain;
89
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
90
+                        {
91
+                            proportion = 1;
92
+                        }
93
+                        else
94
+                        {
95
+                            proportion = Number(dq.value(0)) / reminder.value;
96
+                        }
97
+                        remain = reminder.value - Number(dq.value(0));
98
+                        reminder.completion = proportion;
99
+                        reminder.detail = remain + TTR("remindersreport", " Batches");
100
+                        dq = dq.invalidate();
101
+                    }
102
+                    else if(reminder.condition == "PRODUCTIONHOURS")
103
+                    {
104
+                        var dq = new QSqlQuery;
105
+                        dq.prepare("SELECT extract(epoch FROM (SELECT sum(duration) FROM roasting_log WHERE time > :since) / 3600)");
106
+                        dq.bind(":since", start_time);
107
+                        dq.exec();
108
+                        dq.next();
109
+                        var proportion;
110
+                        var remain;
111
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
112
+                        {
113
+                            proportion = 1;
114
+                        }
115
+                        else
116
+                        {
117
+                            proportion = Number(dq.value(0)) / reminder.value;
118
+                        }
119
+                        remain = reminder.value - Number(dq.value(0));
120
+                        reminder.completion = proportion;
121
+                        reminder.detail = remain.toFixed(1) + TTR("remindersreport", " Hours");
122
+                        dq = dq.invalidate();
123
+                    }
124
+                    e[reminder.dbid] = reminder;
125
+                }
126
+                query = query.invalidate();
127
+            }
128
+            function refresh() {
129
+                populateReminderData();
130
+                var passedData = e.filter(function(n){return n.hasOwnProperty("completion")}).sort(function(a,b){return b.completion-a.completion});
131
+                var buffer = new QBuffer;
132
+                buffer.open(3);
133
+                var output = new XmlWriter(buffer);
134
+                output.writeStartDocument("1.0");
135
+                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">');
136
+                output.writeStartElement("html");
137
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
138
+                output.writeStartElement("head");
139
+                output.writeTextElement("title", TTR("remindersreport", "Reminders"));
140
+                output.writeEndElement();
141
+                output.writeStartElement("body");
142
+                output.writeTextElement("h1", TTR("remindersreport", "Reminders"));
143
+                output.writeStartElement("a");
144
+                output.writeAttribute("href", "typica://script/0");
145
+                output.writeStartElement("div");
146
+                output.writeAttribute("class", "reminder");
147
+                output.writeStartElement("div");
148
+                output.writeAttribute("class", "nocolor");
149
+                output.writeEndElement();
150
+                output.writeStartElement("span");
151
+                output.writeAttribute("class", "title");
152
+                output.writeCDATA(TTR("remindersreport", "New Reminder"));
153
+                output.writeEndElement();
154
+                output.writeEndElement();
155
+                output.writeEndElement();
156
+                output.writeTextElement("script", "e = " + JSON.stringify(passedData));
157
+                var styleFile = new QFile(QSettings.value("config") + "/Scripts/reminders.css");
158
+                styleFile.open(1);
159
+                output.writeTextElement("style", styleFile.readToString());
160
+                styleFile.close();
161
+                output.writeStartElement("script");
162
+                scriptFile = new QFile(QSettings.value("config") + "/Scripts/reminders.js");
163
+                scriptFile.open(1);
164
+                output.writeCDATA(scriptFile.readToString());
165
+                scriptFile.close();
166
+                output.writeEndElement();
167
+                output.writeEndElement();
168
+                output.writeEndDocument();
169
+                report.setContent(buffer);
170
+                buffer.close();
171
+            }
172
+            refresh();
173
+            var doRefresh = function() {
174
+                refresh();
175
+            }
176
+            report.scriptLinkClicked.connect(function(url) {
177
+                var reminder = createWindow("editreminder");
178
+                if(url != "0")
179
+                {
180
+                    reminder.loadData(e[url]);
181
+                }
182
+                reminder.setRefreshFunction(doRefresh);
183
+            });
184
+        ]]>
185
+    </program>
186
+</window>

+ 110
- 61
config/Reports/rwacp.xml View File

1
 <window id="productionreport">
1
 <window id="productionreport">
2
-	<reporttitle>Production:->Recent Average Weekly Coffee Production</reporttitle>
2
+	<reporttitle>Production:->Recent Average Coffee Production</reporttitle>
3
     <layout type="vertical">
3
     <layout type="vertical">
4
         <layout type="horizontal">
4
         <layout type="horizontal">
5
             <label>Sort Order:</label>
5
             <label>Sort Order:</label>
6
             <sqldrop id="sort" />
6
             <sqldrop id="sort" />
7
-			<label>Weight Unit:</label>
8
-			<sqldrop id="unit" />
7
+            <label>Weight Unit:</label>
8
+            <sqldrop id="unit" />
9
+            <label>Batch Type: </label>
10
+            <sqldrop id="batchtype" />
11
+            <label>Days to Average: </label>
12
+            <line id="days" />
13
+            <label>Days to Scale: </label>
14
+            <line id="scale" />
9
             <stretch />
15
             <stretch />
10
         </layout>
16
         </layout>
11
         <webview id="report" />
17
         <webview id="report" />
15
     </menu>
21
     </menu>
16
     <program>
22
     <program>
17
         <![CDATA[
23
         <![CDATA[
18
-            this.windowTitle = "Typica - Recent Average Weekly Coffee Production";
24
+            this.windowTitle = TTR("productionreport", "Typica - Recent Average Coffee Production");
19
             var report = findChildObject(this, 'report');
25
             var report = findChildObject(this, 'report');
20
             var printMenu = findChildObject(this, 'print');
26
             var printMenu = findChildObject(this, 'print');
21
             printMenu.triggered.connect(function() {
27
             printMenu.triggered.connect(function() {
22
                 report.print();
28
                 report.print();
23
             });
29
             });
24
             var sortBox = findChildObject(this, 'sort');
30
             var sortBox = findChildObject(this, 'sort');
25
-            sortBox.addItem("Roasted Coffee A-Z");
26
-            sortBox.addItem("Roasted Coffee Z-A");
27
-            sortBox.addItem("Weekly Use Ascending");
28
-            sortBox.addItem("Weekly Use Descending");
31
+            sortBox.addItem(TTR("productionreport", "Roasted Coffee A-Z"));
32
+            sortBox.addItem(TTR("productionreport", "Roasted Coffee Z-A"));
33
+            sortBox.addItem(TTR("productionreport", "Weekly Use Ascending"));
34
+            sortBox.addItem(TTR("productionreport", "Weekly Use Descending"));
29
             sortBox.currentIndex = QSettings.value("rwacp_sort", 3);
35
             sortBox.currentIndex = QSettings.value("rwacp_sort", 3);
30
-			var unitBox = findChildObject(this, 'unit');
31
-			unitBox.addItem("Kg");
32
-			unitBox.addItem("Lb");
33
-			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
36
+            var unitBox = findChildObject(this, 'unit');
37
+            unitBox.addItem(TTR("productionreport", "Kg"));
38
+            unitBox.addItem(TTR("productionreport", "Lb"));
39
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
40
+            var batchType = findChildObject(this, 'batchtype');
41
+            batchType.addItem(TTR("productionreport", "Any"));
42
+            batchType.addItem(TTR("productionreport", "Production Roasts"));
43
+            batchType.addItem(TTR("productionreport", "Sample Roasts"));
44
+            batchType.currentIndex = QSettings.value("script/racpreport/batchtypefilter", 1);
45
+            batchType['currentIndexChanged(int)'].connect(function() {
46
+                QSettings.setValue("script/racpreport/batchtypefilter",
47
+                                   batchType.currentIndex);
48
+                refresh();
49
+            });
50
+            var daysBox = findChildObject(this, 'days');
51
+            daysBox.text = QSettings.value("script/racpreport/days", "28");
52
+            daysBox.editingFinished.connect(function() {
53
+                if(Number(daysBox.text) < 1) {
54
+                    daysBox.text = QSettings.value("script/racpreport/days");
55
+                }
56
+                QSettings.setValue("script/racpreport/days", daysBox.text);
57
+                refresh();
58
+            });
59
+            var scaleBox = findChildObject(this, 'scale');
60
+            scaleBox.text = QSettings.value("script/racpreport/scale", "7");
61
+            scaleBox.editingFinished.connect(function() {
62
+                if(Number(scaleBox.text) < 1) {
63
+                    scaleBox.text = QSettings.value("script/racpreport/scale");
64
+                }
65
+                QSettings.setValue("script/racpreport/scale", scaleBox.text);
66
+                refresh();
67
+            });
34
             function refresh() {
68
             function refresh() {
35
                 var buffer = new QBuffer;
69
                 var buffer = new QBuffer;
36
                 buffer.open(3);
70
                 buffer.open(3);
40
                 output.writeStartElement("html");
74
                 output.writeStartElement("html");
41
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
75
                 output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
42
                 output.writeStartElement("head");
76
                 output.writeStartElement("head");
43
-                output.writeTextElement("title", "Recent Average Weekly Coffee Production");
77
+                output.writeTextElement("title", TTR("productionreport", "Recent Average Coffee Production"));
44
                 output.writeEndElement();
78
                 output.writeEndElement();
45
                 output.writeStartElement("body");
79
                 output.writeStartElement("body");
46
-                output.writeTextElement("h1", "Recent Average Weekly Coffee Production");
47
-				switch(unitBox.currentIndex)
48
-				{
49
-					case 0:
50
-						output.writeTextElement("p", "This is a report of average weekly coffee production in kilograms over the past 28 days.");
51
-						break;
52
-					case 1:
53
-						output.writeTextElement("p", "This is a report of average weekly coffee production in pounds over the past 28 days.");
54
-						break;
55
-				}
80
+                output.writeTextElement("h1", TTR("productionreport", "Recent Average Coffee Production"));
81
+                switch(unitBox.currentIndex)
82
+                {
83
+                    case 0:
84
+                        output.writeTextElement("p", TTR("productionreport", "This is a report of average coffee production per ") + scaleBox.text + 
85
+                        TTR("productionreport", " days in kilograms over the past ") + daysBox.text +
86
+                        TTR("productionreport", " days."));
87
+                        break;
88
+                    case 1:
89
+                        output.writeTextElement("p", TTR("productionreport", "This is a report of average coffee production per ") + scaleBox.text +
90
+                        TTR("productionreport", " days in pounds over the past ") + daysBox.text +
91
+                        TTR("productionreport", " days."));
92
+                        break;
93
+                }
56
                 output.writeStartElement("table");
94
                 output.writeStartElement("table");
57
                 output.writeAttribute("rules", "groups");
95
                 output.writeAttribute("rules", "groups");
58
                 output.writeAttribute("cellpadding", "3px");
96
                 output.writeAttribute("cellpadding", "3px");
59
                 output.writeStartElement("thead");
97
                 output.writeStartElement("thead");
60
                 output.writeStartElement("tr");
98
                 output.writeStartElement("tr");
61
-                output.writeTextElement("th", "Roasted Coffee");
62
-                output.writeTextElement("th", "Weekly Use");
99
+                output.writeTextElement("th", TTR("productionreport", "Roasted Coffee"));
100
+                output.writeTextElement("th", TTR("productionreport", "Weekly Use"));
63
                 output.writeEndElement();
101
                 output.writeEndElement();
64
                 output.writeEndElement();
102
                 output.writeEndElement();
65
                 output.writeStartElement("tbody");
103
                 output.writeStartElement("tbody");
66
-				var q = "SELECT (SELECT name FROM items WHERE id = roasted_id) AS name, ((sum(roasted_quantity) / 4) / :conversion)::numeric(18,2) AS weekly FROM roasting_log WHERE time > current_date - integer '28' AND roasted_quantity > 0 GROUP BY roasted_id ORDER BY "
104
+                var batchClause = "";
105
+                switch(batchType.currentIndex) {
106
+                    case 1:
107
+                        batchClause = " AND transaction_type = 'ROAST'";
108
+                        break;
109
+                    case 2:
110
+                        batchClause = " AND transaction_type = 'SAMPLEROAST'";
111
+                        break;
112
+                }
113
+                var q = "SELECT (SELECT name FROM items WHERE id = roasted_id) AS name, ((sum(roasted_quantity) / :scale) / :conversion)::numeric(18,2) AS weekly FROM roasting_log WHERE time > current_date - integer '" + daysBox.text + "' AND roasted_quantity > 0 " + batchClause + " GROUP BY roasted_id ORDER BY "
67
                 switch(sortBox.currentIndex)
114
                 switch(sortBox.currentIndex)
68
                 {
115
                 {
69
                     case 0:
116
                     case 0:
70
-						q += "name ASC";
117
+                        q += "name ASC";
71
                         break;
118
                         break;
72
                     case 1:
119
                     case 1:
73
-						q += "name DESC";
74
-						break;
120
+                        q += "name DESC";
121
+                        break;
75
                     case 2:
122
                     case 2:
76
-						q += "weekly ASC";
77
-						break;
123
+                        q += "weekly ASC";
124
+                        break;
78
                     case 3:
125
                     case 3:
79
-						q += "weekly DESC";
80
-						break;
126
+                        q += "weekly DESC";
127
+                        break;
128
+                }
129
+                var query = new QSqlQuery();
130
+                query.prepare(q);
131
+                switch(unitBox.currentIndex)
132
+                {
133
+                    case 0:
134
+                        query.bind(":conversion", 2.2);
135
+                        break;
136
+                    case 1:
137
+                        query.bind(":conversion", 1);
138
+                        break;
81
                 }
139
                 }
82
-				var query = new QSqlQuery();
83
-				query.prepare(q);
84
-				switch(unitBox.currentIndex)
85
-				{
86
-					case 0:
87
-						query.bind(":conversion", 2.2);
88
-						break;
89
-					case 1:
90
-						query.bind(":conversion", 1);
91
-						break;
92
-				}
140
+                query.bind(":scale", Number(daysBox.text)/Number(scaleBox.text));
93
                 query.exec();
141
                 query.exec();
94
                 while(query.next())
142
                 while(query.next())
95
                 {
143
                 {
100
                 }
148
                 }
101
                 output.writeEndElement();
149
                 output.writeEndElement();
102
                 output.writeStartElement("tfoot")
150
                 output.writeStartElement("tfoot")
103
-                output.writeTextElement("th", "Total");
104
-                query.prepare("SELECT (sum(roasted_quantity) / 4 / :conversion)::numeric(18,2) FROM roasting_log WHERE time > current_date - integer '28' AND roasted_quantity > 0");
151
+                output.writeTextElement("th", TTR("productionreport", "Total:"));
152
+                query.prepare("SELECT ((sum(roasted_quantity) / :scale) / :conversion)::numeric(18,2) FROM roasting_log WHERE time > current_date - integer '" + daysBox.text + "' AND roasted_quantity > 0 " + batchClause);
105
                 switch(unitBox.currentIndex)
153
                 switch(unitBox.currentIndex)
106
-				{
107
-					case 0:
108
-						query.bind(":conversion", 2.2);
109
-						break;
110
-					case 1:
111
-						query.bind(":conversion", 1);
112
-						break;
113
-				}
114
-				query.exec();
115
-				query.next();
154
+                {
155
+                    case 0:
156
+                        query.bind(":conversion", 2.2);
157
+                        break;
158
+                    case 1:
159
+                        query.bind(":conversion", 1);
160
+                        break;
161
+                }
162
+                query.bind(":scale", Number(daysBox.text)/Number(scaleBox.text));
163
+                query.exec();
164
+                query.next();
116
                 output.writeTextElement("td", query.value(0));
165
                 output.writeTextElement("td", query.value(0));
117
                 query = query.invalidate();
166
                 query = query.invalidate();
118
-				output.writeEndElement();
167
+                output.writeEndElement();
119
                 output.writeEndElement();
168
                 output.writeEndElement();
120
                 output.writeEndElement();
169
                 output.writeEndElement();
121
                 output.writeEndElement();
170
                 output.writeEndElement();
128
                 QSettings.setValue("rwacp_sort", sortBox.currentIndex);
177
                 QSettings.setValue("rwacp_sort", sortBox.currentIndex);
129
                 refresh();
178
                 refresh();
130
             });
179
             });
131
-			unitBox['currentIndexChanged(int)'].connect(function() {
132
-				QSettings.setValue("script/report_unit", unitBox.currentIndex);
133
-				refresh();
134
-			});
180
+            unitBox['currentIndexChanged(int)'].connect(function() {
181
+                QSettings.setValue("script/report_unit", unitBox.currentIndex);
182
+                refresh();
183
+            });
135
         ]]>
184
         ]]>
136
     </program>
185
     </program>
137
 </window>
186
 </window>

+ 68
- 0
config/Scripts/reminders.css View File

1
+body{
2
+    background-color: #EDE3C0;
3
+}
4
+.reminder{
5
+    background-color: #EEEEEE;
6
+    display: block;
7
+    margin-left: 10px;
8
+    margin-right: 10px;
9
+    margin-top: 10px;
10
+    max-width: 300px;
11
+    padding-right: 5px;
12
+    padding-bottom: 5px;
13
+}
14
+.green{
15
+    width: 150px;
16
+    background-color: #00FF00;
17
+    height: 10px;
18
+    display: block;
19
+}
20
+.yellow{
21
+    width: 150px;
22
+    background-color: #FFFF00;
23
+    height: 10px;
24
+    display: block;
25
+}
26
+.orange{
27
+    width: 150px;
28
+    background-color: #FFA500;
29
+    height: 10px;
30
+    display: block;
31
+}
32
+.red{
33
+    width: 150px;
34
+    background-color: #FF0000;
35
+    height: 10px;
36
+    display: block;
37
+}
38
+.nocolor{
39
+    height: 10px;
40
+    display: block;
41
+}
42
+a{
43
+    text-decoration: none;
44
+    color: #000000;
45
+    display: block;
46
+    max-width: 300px;
47
+}
48
+.reminder span{
49
+    margin-left: 5px;
50
+    display: block;
51
+}
52
+.progress{
53
+    float: left;
54
+    color: #708090;
55
+    font-size: 80%
56
+}
57
+.detail{
58
+    float: right;
59
+    color: #708090;
60
+    font-size: 80%
61
+}
62
+.clearfix{
63
+    clear: both;
64
+}
65
+.title{
66
+    margin-bottom: 5px;
67
+    margin-top: 3px;
68
+}

+ 23
- 0
config/Scripts/reminders.js View File

1
+e.forEach(function(item) {
2
+    var element = document.createElement("a");
3
+    element.href="typica://script/" + item.dbid
4
+    var colordiv;
5
+    if(item.completion >= 1)
6
+    {
7
+        colordiv = '<div class="red"></div>';
8
+    }
9
+    else
10
+    {
11
+        colordiv = '<div class="orange"></div>';
12
+    }
13
+    if(item.completion < 0.6)
14
+    {
15
+        colordiv = '<div class="yellow"></div>';
16
+    }
17
+    if(item.completion < 0.3)
18
+    {
19
+        colordiv = '<div class="green"></div>';
20
+    }
21
+    element.innerHTML = '<div class="reminder">' + colordiv + '<span class="title">' + item.title + '</span><span class="progress">' + Math.floor(item.completion * 100) + '%</span><span class="detail">' + item.detail + '</span><div class="clearfix"></div></div>';
22
+    document.body.appendChild(element);
23
+})

BIN
config/Translations/config.de.qm View File


+ 2810
- 0
config/Translations/config.de.ts
File diff suppressed because it is too large
View File


+ 2309
- 0
config/Translations/config.ts
File diff suppressed because it is too large
View File


+ 0
- 178
config/Windows/batchdetails.xml View File

1
-<window id="batchDetails">
2
-    <layout type="vertical">
3
-        <layout type="horizontal">
4
-            <label>Time:</label>
5
-            <line editable="false" id="time" />
6
-        </layout>
7
-        <layout type="horizontal">
8
-            <label>Name:</label>
9
-            <line editable="false" id="name" />
10
-        </layout>
11
-        <layout type="horizontal">
12
-            <label>Green:</label>
13
-            <line editable="false" id="green" />
14
-        </layout>
15
-        <layout type="horizontal">
16
-            <label>Roasted:</label>
17
-            <line editable="false" id="roasted" />
18
-        </layout>
19
-        <layout type="horizontal">
20
-            <label>Duration:</label>
21
-            <line editable="false" id="duration" />
22
-        </layout>
23
-        <layout type="horizontal">
24
-            <label>Approval:</label>
25
-            <line editable="false" id="approval" />
26
-        </layout>
27
-        <layout type="horizontal">
28
-            <label>Files:</label>
29
-			<line id="files" />
30
-		</layout>
31
-        <layout type="vertical">
32
-            <button type="push" id="target" name="Load profile as target" />
33
-            <button type="push" id="view" name="View profile" />
34
-            <button type="push" id="compare" name="Compare profile" />
35
-        </layout>
36
-    </layout>
37
-    <program>
38
-        <![CDATA[
39
-			var widget = this;
40
-			var target = findChildObject(this, 'target');
41
-			var batchTime = findChildObject(this, 'time');
42
-			var filesfield = findChildObject(this, 'files');
43
-            var compare = findChildObject(this, 'compare');
44
-			if(typeof(navigationwindow.loggingWindow) == "undefined")
45
-			{
46
-				compare.enabled = false;
47
-				target.enabled = false;
48
-			}
49
-            compare.clicked.connect(function() {
50
-                files = filesfield.text;
51
-                files = files.replace("{", "(");
52
-                files = files.replace("}", ")");
53
-                var q = "SELECT file, name FROM files WHERE id IN ";
54
-                q = q + files;
55
-                q = q + " AND type = 'profile'";
56
-                query = new QSqlQuery();
57
-                query.exec(q);
58
-                query.next();
59
-                var buffer = new QBuffer(query.value(0));
60
-                var pname = query.value(1);
61
-                query = query.invalidate();
62
-                var startSeries = Number(QSettings.value('cseries', 3));
63
-                var nextSeries = startSeries + 2;
64
-                QSettings.setValue('cseries', nextSeries);
65
-                var input = new XMLInput(buffer, startSeries);
66
-                var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
67
-                input.measure.connect(graph.newMeasurement);
68
-                input.input();
69
-            });
70
-			target.clicked.connect(function() {
71
-				files = filesfield.text;
72
-				files = files.replace("{", "(");
73
-				files = files.replace("}", ")");
74
-				var q = "SELECT file, name FROM files WHERE id IN ";
75
-				q = q + files;
76
-				q = q + " AND type = 'profile'";
77
-				query = new QSqlQuery();
78
-				query.exec(q);
79
-				query.next();
80
-				var targetseries = -1;
81
-				var buffer = new QBuffer(query.value(0));
82
-				var pname = query.value(1);
83
-                query = query.invalidate();
84
-				var input = new XMLInput(buffer, 1);
85
-				var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
86
-				var log = findChildObject(navigationwindow.loggingWindow, 'log');
87
-				log.clear();
88
-				graph.clear();
89
-				input.newTemperatureColumn.connect(log.setHeaderData);
90
-				input.newTemperatureColumn.connect(function(col, text) {
91
-					if(text == navigationwindow.loggingWindow.targetcolumnname)
92
-					{
93
-						targetseries = col;
94
-					}
95
-				});
96
-				input.newAnnotationColumn.connect(log.setHeaderData);
97
-				input.measure.connect(graph.newMeasurement);
98
-				input.measure.connect(log.newMeasurement);
99
-				input.measure.connect(function(data, series) {
100
-					if(series == targetseries)
101
-					{
102
-						targetDetector.newMeasurement(data);
103
-					}
104
-				});
105
-				var lc;
106
-				input.lastColumn.connect(function(c) {
107
-					lc = c;
108
-					QSettings.setValue("liveColumn", c + 1);
109
-					navigationwindow.loggingWindow.postLoadColumnSetup(c);
110
-				});
111
-				input.annotation.connect(log.newAnnotation);
112
-				input.annotation.connect(function(note, tcol, ncol) {
113
-					for(var i = tcol; i < ncol; i++) {
114
-						log.newAnnotation(note, i, ncol);
115
-					}
116
-				});
117
-				navigationwindow.loggingWindow.windowTitle = "Typica - " + pname;
118
-				navigationwindow.loggingWindow.raise();
119
-				navigationwindow.loggingWindow.activateWindow();
120
-				input.input();
121
-				log.newAnnotation("End", 1, lc);
122
-			});
123
-			var viewbutton = findChildObject(this, 'view');
124
-			viewbutton.clicked.connect(function() {
125
-				files = filesfield.text;
126
-				files = files.replace("{", "(");
127
-				files = files.replace("}", ")");
128
-				var q = "SELECT file, name FROM files WHERE id IN ";
129
-				q = q + files;
130
-				q = q + " AND type = 'profile'";
131
-				query = new QSqlQuery();
132
-				query.exec(q);
133
-				if(query.next()) {
134
-					var viewer = createWindow('offline');
135
-					var buffer = new QBuffer(query.value(0));
136
-					var pname = query.value(1);
137
-					query = query.invalidate();
138
-					var input = new XMLInput(buffer, 1);
139
-					var graph = findChildObject(viewer, 'graph');
140
-					var log = findChildObject(viewer, 'log');
141
-					input.newTemperatureColumn.connect(log.setHeaderData);
142
-					input.newTemperatureColumn.connect(function(column) {
143
-						viewer.saveTemperatureColumns.push(column);
144
-					});
145
-					input.newAnnotationColumn.connect(log.setHeaderData);
146
-					input.newAnnotationColumn.connect(function(column) {
147
-						viewer.saveAnnotationColumns.push(column);
148
-					});
149
-					input.measure.connect(graph.newMeasurement);
150
-					input.measure.connect(log.newMeasurement);
151
-					input.annotation.connect(log.newAnnotation);
152
-					var lc;
153
-					input.lastColumn.connect(function(c) {
154
-						lc = c;
155
-						if(c < 3)
156
-						{
157
-							log.setHeaderData(3, "");
158
-						}
159
-					});
160
-					input.annotation.connect(function(note, tcol, ncol) {
161
-						for(var i = tcol; i < ncol; i++) {
162
-							log.newAnnotation(note, i, ncol);
163
-						}
164
-					});
165
-					viewer.windowTitle = "Typica - " + pname;
166
-					viewer.raise();
167
-					viewer.activateWindow();
168
-					input.input();
169
-					log.newAnnotation("End", 1, lc);
170
-				}
171
-				else {
172
-					print("Query returned no results");
173
-				}
174
-				query = query.invalidate();
175
-            });
176
-        ]]>
177
-    </program>
178
-</window>

+ 336
- 325
config/Windows/batchdetailsnew.xml View File

2
     <layout type="vertical">
2
     <layout type="vertical">
3
         <layout type="horizontal">
3
         <layout type="horizontal">
4
             <button type="push" id="target" name="Load profile as target" />
4
             <button type="push" id="target" name="Load profile as target" />
5
-			<button type="push" id="viewprofile" name="View profile" />
6
-			<button type="push" id="compare" name="Compare profile" />
7
-			<button type="push" id="edit" name="Edit" />
5
+            <button type="push" id="viewprofile" name="View profile" />
6
+            <button type="push" id="compare" name="Compare profile" />
7
+            <button type="push" id="edit" name="Edit" />
8
         </layout>
8
         </layout>
9
         <webview id="view" />
9
         <webview id="view" />
10
     </layout>
10
     </layout>
11
 	<menu name="File">
11
 	<menu name="File">
12
-		<item id="save" shortcut="Ctrl+S">Save…</item>
12
+            <item id="save" shortcut="Ctrl+S">Save...</item>
13
+            <item id="print" shortcut="Ctrl+P">Print...</item>
13
 	</menu>
14
 	</menu>
14
     <program>
15
     <program>
15
         <![CDATA[
16
         <![CDATA[
16
-			var window = this;
17
-			dataView = findChildObject(this, 'view');
18
-			var fileID;
19
-			var target = findChildObject(this, 'target');
20
-			var compare = findChildObject(this, 'compare');
21
-			var edit = findChildObject(this, 'edit');
22
-			edit.enabled = false;
23
-			if(typeof(Windows.loggingWindow) == "undefined") {
24
-				compare.enabled = false;
25
-				target.enabled = false;
26
-			}
27
-			var tableReference;
28
-			var rowReference;
29
-			var batchTime;
30
-			var machine;
31
-			var approval;
32
-			var annotation;
33
-			edit.clicked.connect(function() {
34
-				var editWindow = createWindow("editBatchDetails");
35
-				editWindow.loadData(window, tableReference, rowReference, batchTime, machine, approval, annotation);
36
-			});
37
-			compare.clicked.connect(function() {
38
-				var query = new QSqlQuery;
39
-				query.prepare("SELECT file, name FROM files WHERE id = :id");
40
-				query.bind(":id", Number(fileID));
41
-				query.exec();
42
-				query.next();
43
-				var buffer = new QBuffer(query.value(0));
17
+            var window = this;
18
+            var unit = QSettings.value("script/history_unit", 1);
19
+            var conversion = 1;
20
+            if(unit == 0)
21
+            {
22
+                conversion = 2.2;
23
+            }
24
+            var unitText = (unit == 0 ? TTR("batchDetails", "Kg") :
25
+                           TTR("batchDetails", "Lb"));
26
+            dataView = findChildObject(this, 'view');
27
+            var printMenu = findChildObject(this, 'print');
28
+            printMenu.triggered.connect(function() {
29
+                dataView.print();
30
+            });
31
+            var fileID;
32
+            var target = findChildObject(this, 'target');
33
+            var compare = findChildObject(this, 'compare');
34
+            var edit = findChildObject(this, 'edit');
35
+            edit.enabled = false;
36
+            if(typeof(Windows.loggingWindow) == "undefined") {
37
+                compare.enabled = false;
38
+                target.enabled = false;
39
+            }
40
+            var tableReference;
41
+            var rowReference;
42
+            var batchTime;
43
+            var machine;
44
+            var approval;
45
+            var annotation;
46
+            var roastWeight;
47
+            edit.clicked.connect(function() {
48
+                var editWindow = createWindow("editBatchDetails");
49
+                editWindow.loadData(window, tableReference, rowReference, batchTime, machine, approval, annotation, roastWeight, unit);
50
+            });
51
+            compare.clicked.connect(function() {
52
+                var query = new QSqlQuery;
53
+                query.prepare("SELECT file, name FROM files WHERE id = :id");
54
+                query.bind(":id", Number(fileID));
55
+                query.exec();
56
+                query.next();
57
+                var buffer = new QBuffer(query.value(0));
44
                 var pname = query.value(1);
58
                 var pname = query.value(1);
45
                 query = query.invalidate();
59
                 query = query.invalidate();
46
                 var startSeries = Number(QSettings.value('cseries', 3));
60
                 var startSeries = Number(QSettings.value('cseries', 3));
50
                 var graph = findChildObject(Windows.loggingWindow, 'graph');
64
                 var graph = findChildObject(Windows.loggingWindow, 'graph');
51
                 input.measure.connect(graph.newMeasurement);
65
                 input.measure.connect(graph.newMeasurement);
52
                 input.input();
66
                 input.input();
53
-				query = query.invalidate();
54
-			});
55
-			target.clicked.connect(function() {
56
-				var query = new QSqlQuery;
57
-				query.prepare("SELECT file, name FROM files WHERE id = :id");
58
-				query.bind(":id", Number(fileID));
59
-				query.exec();
60
-				query.next();
61
-				var buffer = new QBuffer(query.value(0));
62
-				var pname = query.value(1);
63
                 query = query.invalidate();
67
                 query = query.invalidate();
64
-				var input = new XMLInput(buffer, 1);
65
-				var graph = findChildObject(Windows.loggingWindow, 'graph');
66
-				var log = findChildObject(Windows.loggingWindow, 'log');
67
-				log.clear();
68
-				graph.clear();
69
-				input.newTemperatureColumn.connect(log.setHeaderData);
70
-				input.newTemperatureColumn.connect(function(col, text) {
71
-					if(text == Windows.loggingWindow.targetcolumnname)
72
-					{
73
-						targetseries = col;
74
-					}
75
-				});
76
-				input.newAnnotationColumn.connect(log.setHeaderData);
77
-				input.measure.connect(graph.newMeasurement);
78
-				input.measure.connect(log.newMeasurement);
79
-				input.measure.connect(function(data, series) {
80
-					if(series == targetseries)
81
-					{
82
-						targetDetector.newMeasurement(data);
83
-					}
84
-				});
85
-				var lc;
86
-				input.lastColumn.connect(function(c) {
87
-					lc = c;
88
-					QSettings.setValue("liveColumn", c + 1);
89
-					Windows.loggingWindow.postLoadColumnSetup(c);
90
-				});
91
-				input.annotation.connect(log.newAnnotation);
92
-				input.annotation.connect(function(note, tcol, ncol) {
93
-					for(var i = tcol; i < ncol; i++) {
94
-						log.newAnnotation(note, i, ncol);
95
-					}
96
-				});
97
-				Windows.loggingWindow.windowTitle = "Typica - " + pname;
98
-				Windows.loggingWindow.raise();
99
-				Windows.loggingWindow.activateWindow();
100
-				input.input();
101
-				log.newAnnotation("End", 1, lc);
102
-				query = query.invalidate();
103
-			});
104
-			var viewbutton = findChildObject(this, 'viewprofile');
105
-			viewbutton.clicked.connect(function() {
106
-				var query = new QSqlQuery;
107
-				query.prepare("SELECT file, name FROM files WHERE id = :id");
108
-				query.bind(":id", Number(fileID));
109
-				query.exec();
110
-				if(query.next()) {
111
-					var viewer = createWindow('offline');
112
-					var buffer = new QBuffer(query.value(0));
113
-					var pname = query.value(1);
114
-					query = query.invalidate();
115
-					var input = new XMLInput(buffer, 1);
116
-					var graph = findChildObject(viewer, 'graph');
117
-					var log = findChildObject(viewer, 'log');
118
-					input.newTemperatureColumn.connect(log.setHeaderData);
119
-					input.newTemperatureColumn.connect(function(column) {
120
-						viewer.saveTemperatureColumns.push(column);
121
-					});
122
-					input.newAnnotationColumn.connect(log.setHeaderData);
123
-					input.newAnnotationColumn.connect(function(column) {
124
-						viewer.saveAnnotationColumns.push(column);
125
-					});
126
-					input.measure.connect(graph.newMeasurement);
127
-					input.measure.connect(log.newMeasurement);
128
-					input.annotation.connect(log.newAnnotation);
129
-					var lc;
130
-					input.lastColumn.connect(function(c) {
131
-						lc = c;
132
-						if(c < 3)
133
-						{
134
-							log.setHeaderData(3, "");
135
-						}
136
-					});
137
-					input.annotation.connect(function(note, tcol, ncol) {
138
-						for(var i = tcol; i < ncol; i++) {
139
-							log.newAnnotation(note, i, ncol);
140
-						}
141
-					});
142
-					viewer.windowTitle = "Typica - " + pname;
143
-					viewer.raise();
144
-					viewer.activateWindow();
145
-					graph.updatesEnabled = false;
146
-					log.updatesEnabled = false;
147
-					input.input();
148
-					log.updatesEnabled = true;
149
-					graph.updatesEnabled = true;
150
-					log.newAnnotation("End", 1, lc);
151
-				}
152
-				else {
153
-					print("Query returned no results");
154
-				}
155
-				query = query.invalidate();
156
-			});
157
-			window.loadData = function(table, row) {
158
-				tableReference = table;
159
-				rowReference = row;
160
-				var buffer = new QBuffer;
161
-				buffer.open(3);
162
-				var output = new XmlWriter(buffer);
163
-				output.writeStartDocument("1.0");
164
-				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">');
165
-				output.writeStartElement("html");
166
-				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
167
-				output.writeStartElement("head");
168
-				output.writeTextElement("title", "Batch Details");
169
-				output.writeEndElement();
170
-				output.writeStartElement("body");
171
-				output.writeStartElement("div");
172
-				output.writeAttribute("style", "float: left; padding-right: 10px");
173
-				output.writeStartElement("p");
174
-				output.writeTextElement("strong", "Roasted Coffee: ");
175
-				output.writeTextElement("span", table.data(row, 2));
176
-				output.writeEndElement();
177
-				output.writeEndElement();
178
-				output.writeStartElement("div");
179
-				output.writeAttribute("style", "float: left");
180
-				output.writeStartElement("p");
181
-				output.writeTextElement("strong", "Roasted On: ");
182
-				batchTime = table.data(row, 0);
183
-				output.writeTextElement("span", batchTime);
184
-				output.writeEndElement();
185
-				output.writeStartElement("p");
186
-				output.writeTextElement("strong", "Batch Duration: ");
187
-				output.writeTextElement("span", table.data(row, 6));
188
-				output.writeEndElement();
189
-				output.writeEndElement();
190
-				output.writeStartElement("div");
191
-				output.writeAttribute("style", "clear: both");
192
-				output.writeEndElement();
193
-				output.writeStartElement("table");
194
-				output.writeStartElement("thead");
195
-				output.writeStartElement("tr");
196
-				output.writeTextElement("th", "Green Coffee");
197
-				output.writeTextElement("th", "Weight (lb)");
198
-				output.writeEndElement();
199
-				output.writeEndElement();
200
-				output.writeStartElement("tbody");
201
-				var query = new QSqlQuery();
202
-				query.prepare("SELECT unroasted_id, unroasted_quantity, approval, files, annotation FROM roasting_log WHERE time = :time AND machine = :machine");
203
-				query.bind(":time", batchTime);
204
-				machine = table.data(row, 1);
205
-				query.bind(":machine", machine);
206
-				query.exec();
207
-				query.next();
208
-				approval = query.value(2);
209
-				annotation = query.value(4);
210
-				var items = sqlToArray(query.value(0));
211
-				var quantities = sqlToArray(query.value(1));
212
-				var nameQuery = new QSqlQuery();
213
-				nameQuery.prepare("SELECT name FROM items WHERE id = :id");
214
-				for(var i = 0; i < items.length; i++) {
215
-					output.writeStartElement("tr");
216
-					nameQuery.bind(":id", items[i]);
217
-					nameQuery.exec();
218
-					nameQuery.next();
219
-					output.writeTextElement("td", nameQuery.value(0) + " (" + items[i] + ")");
220
-					output.writeStartElement("td");
221
-					output.writeAttribute("align", "center");
222
-					output.writeCharacters(quantities[i]);
223
-					output.writeEndElement();
224
-					output.writeEndElement();
225
-				}
226
-				nameQuery = nameQuery.invalidate();
227
-				output.writeEndElement();
228
-				output.writeStartElement("tfoot");
229
-				output.writeStartElement("tr");
230
-				output.writeStartElement("td");
231
-				output.writeAttribute("align", "right");
232
-				output.writeTextElement("strong", "Green Total:");
233
-				output.writeEndElement();
234
-				output.writeStartElement("td");
235
-				output.writeAttribute("align", "center");
236
-				output.writeCharacters(table.data(row, 3));
237
-				output.writeEndElement();
238
-				output.writeEndElement();
239
-				output.writeStartElement("tr");
240
-				output.writeStartElement("td");
241
-				output.writeAttribute("align", "right");
242
-				output.writeTextElement("strong", "Roasted Weight:");
243
-				output.writeEndElement();
244
-				output.writeStartElement("td");
245
-				output.writeAttribute("align", "center");
246
-				output.writeCharacters(table.data(row, 4));
247
-				output.writeEndElement();
248
-				output.writeEndElement();
249
-				output.writeStartElement("tr");
250
-				output.writeStartElement("td");
251
-				output.writeAttribute("align", "right");
252
-				output.writeTextElement("strong", "Weight Loss:");
253
-				output.writeEndElement();
254
-				output.writeStartElement("td");
255
-				output.writeAttribute("align", "center");
256
-				output.writeCharacters(table.data(row, 5));
257
-				output.writeCharacters("%");
258
-				output.writeEndElement();
259
-				output.writeEndElement();
260
-				output.writeEndElement();
261
-				output.writeEndElement();
262
-				output.writeStartElement("p");
263
-				output.writeTextElement("strong", "Approved: ");
264
-				output.writeCharacters(approval);
265
-				output.writeEndElement();
266
-				output.writeStartElement("p");
267
-				output.writeTextElement("strong", "Files: ");
268
-				output.writeCharacters(query.value(3));
269
-				output.writeEndElement();
270
-				output.writeStartElement("p");
271
-				output.writeTextElement("strong", "Annotations:");
272
-				var files = sqlToArray(query.value(3));
273
-				var annotations = annotationFromRecord(files[0]);
274
-				fileID = files[0];
275
-				var buffer2 = new QBuffer("<points>"+annotations+"</points>");
276
-				buffer2.open(1);
277
-				var colQuery = new XQuery;
278
-				colQuery.bind("profile", buffer2);
279
-				colQuery.setQuery('for $i in doc($profile)//tuple[1]/temperature/@series return (string($i), ";")');
280
-				var result = colQuery.exec();
281
-				buffer2.close();
282
-				var seriesHeaders = new Array();
283
-				seriesHeaders.push("Time");
284
-				var records = result.split(";");
285
-				for(var i = 0; i < records.length - 1; i++) {
286
-					seriesHeaders.push(records[i].replace(/^\s+|\s+$/g,""));
287
-				}
288
-				seriesHeaders.push("Note");
289
-				output.writeStartElement("table");
290
-				output.writeStartElement("thead");
291
-				output.writeStartElement("tr");
292
-				for(var i = 0; i < seriesHeaders.length; i++) {
293
-					output.writeTextElement("th", seriesHeaders[i]);
294
-				}
295
-				output.writeEndElement();
296
-				output.writeEndElement();
297
-				buffer2.open(1);
298
-				var rq = 'for $t in doc($profile) //tuple return (string($t/time), ";", ';
299
-				for(var i = 0; i < seriesHeaders.length - 2; i++) {
300
-					rq = rq + 'string($t/temperature[' + Number(i+1) + ']), ";", ';
301
-				}
302
-				rq = rq + 'string($t/annotation), "~")';
303
-				colQuery.setQuery(rq);
304
-				var annotationData = colQuery.exec();
305
-				colQuery = colQuery.invalidate();
306
-				buffer2.close();
307
-				output.writeStartElement("tbody");
308
-				var annotationRecords = annotationData.split("~");
309
-				for(var i = 0; i < annotationRecords.length - 1; i++) {
310
-					output.writeStartElement("tr");
311
-					var annotationRow = annotationRecords[i].split(";");
312
-					for(var j = 0; j < annotationRow.length; j++) {
313
-						output.writeStartElement("td");
314
-						output.writeAttribute("style", "border-left: 1px solid #000000");
315
-						if(j > 0) {
316
-							output.writeAttribute("align", "center");
317
-						}
318
-						if(j > 0 && j < annotationRow.length - 1) {
319
-							output.writeCharacters(Number(annotationRow[j].replace(/^\s+|\s+$/g,"")).toFixed(2));
320
-						}
321
-						else {
322
-							output.writeCharacters(annotationRow[j].replace(/^\s+|\s+$/g,""));
323
-						}
324
-						output.writeEndElement();
325
-					}
326
-					output.writeEndElement();
327
-				}
328
-				output.writeEndElement();
329
-				output.writeEndElement();
330
-				output.writeCharacters(annotation);
331
-				output.writeEndElement();
332
-				output.writeEndElement();
333
-				output.writeEndElement();
334
-				output.writeEndDocument();
335
-				dataView.setContent(buffer);
336
-				buffer.close();
337
-				query = query.invalidate();
338
-				edit.enabled = true;
339
-			};
340
-			var saveMenu = findChildObject(this, 'save');
341
-			saveMenu.triggered.connect(function() {
342
-				var filename = QFileDialog.getSaveFileName(window, "Save Log As…", QSettings.value("script/lastDir", "") + "/");
343
-				if(filename != "") {
344
-					saveFileFromDatabase(fileID, filename);
345
-				}
346
-			});
68
+            });
69
+            target.clicked.connect(function() {
70
+                var query = new QSqlQuery;
71
+                query.prepare("SELECT file, name FROM files WHERE id = :id");
72
+                query.bind(":id", Number(fileID));
73
+                query.exec();
74
+                query.next();
75
+                var buffer = new QBuffer(query.value(0));
76
+                var pname = query.value(1);
77
+                query = query.invalidate();
78
+                var input = new XMLInput(buffer, 1);
79
+                var graph = findChildObject(Windows.loggingWindow, 'graph');
80
+                var log = findChildObject(Windows.loggingWindow, 'log');
81
+                log.clear();
82
+                graph.clear();
83
+                input.newTemperatureColumn.connect(log.setHeaderData);
84
+                input.newTemperatureColumn.connect(function(col, text) {
85
+                    if(text == Windows.loggingWindow.targetcolumnname) {
86
+                        targetseries = col;
87
+                    }
88
+                });
89
+                input.newAnnotationColumn.connect(log.setHeaderData);
90
+                input.measure.connect(graph.newMeasurement);
91
+                input.measure.connect(log.newMeasurement);
92
+                input.measure.connect(function(data, series) {
93
+                    if(series == targetseries) {
94
+                        targetDetector.newMeasurement(data);
95
+                    }
96
+                });
97
+                var lc;
98
+                input.lastColumn.connect(function(c) {
99
+                    lc = c;
100
+                    QSettings.setValue("liveColumn", c + 1);
101
+                    Windows.loggingWindow.postLoadColumnSetup(c);
102
+                });
103
+                input.annotation.connect(log.newAnnotation);
104
+                input.annotation.connect(function(note, tcol, ncol) {
105
+                    for(var i = tcol; i < ncol; i++) {
106
+                        log.newAnnotation(note, i, ncol);
107
+                    }
108
+                });
109
+                Windows.loggingWindow.windowTitle = "Typica - " + pname;
110
+                Windows.loggingWindow.raise();
111
+                Windows.loggingWindow.activateWindow();
112
+                input.input();
113
+                log.newAnnotation(TTR("batchDetails", "End"), 1, lc);
114
+                query = query.invalidate();
115
+            });
116
+            var viewbutton = findChildObject(this, 'viewprofile');
117
+            viewbutton.clicked.connect(function() {
118
+                var query = new QSqlQuery;
119
+                query.prepare("SELECT file, name FROM files WHERE id = :id");
120
+                query.bind(":id", Number(fileID));
121
+                query.exec();
122
+                if(query.next()) {
123
+                    var viewer = createWindow('offline');
124
+                    var buffer = new QBuffer(query.value(0));
125
+                    var pname = query.value(1);
126
+                    query = query.invalidate();
127
+                    var input = new XMLInput(buffer, 1);
128
+                    var graph = findChildObject(viewer, 'graph');
129
+                    var log = findChildObject(viewer, 'log');
130
+                    input.newTemperatureColumn.connect(log.setHeaderData);
131
+                    input.newTemperatureColumn.connect(function(column) {
132
+                        viewer.saveTemperatureColumns.push(column);
133
+                    });
134
+                    input.newAnnotationColumn.connect(log.setHeaderData);
135
+                    input.newAnnotationColumn.connect(function(column) {
136
+                        viewer.saveAnnotationColumns.push(column);
137
+                    });
138
+                    input.measure.connect(graph.newMeasurement);
139
+                    input.measure.connect(log.newMeasurement);
140
+                    input.annotation.connect(log.newAnnotation);
141
+                    var lc;
142
+                    input.lastColumn.connect(function(c) {
143
+                        lc = c;
144
+                        if(c < 3) {
145
+                            log.setHeaderData(3, "");
146
+                        }
147
+                    });
148
+                    input.annotation.connect(function(note, tcol, ncol) {
149
+                        for(var i = tcol; i < ncol; i++) {
150
+                            log.newAnnotation(note, i, ncol);
151
+                        }
152
+                    });
153
+                    viewer.windowTitle = "Typica - " + pname;
154
+                    viewer.raise();
155
+                    viewer.activateWindow();
156
+                    graph.updatesEnabled = false;
157
+                    log.updatesEnabled = false;
158
+                    input.input();
159
+                    log.updatesEnabled = true;
160
+                    graph.updatesEnabled = true;
161
+                    log.newAnnotation("End", 1, lc);
162
+                }
163
+                query = query.invalidate();
164
+            });
165
+            window.loadData = function(table, row) {
166
+                tableReference = table;
167
+                rowReference = row;
168
+                var buffer = new QBuffer;
169
+                buffer.open(3);
170
+                var output = new XmlWriter(buffer);
171
+                output.writeStartDocument("1.0");
172
+                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">');
173
+                output.writeStartElement("html");
174
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
175
+                output.writeStartElement("head");
176
+                output.writeTextElement("title", TTR("batchDetails", "Batch Details"));
177
+                output.writeEndElement();
178
+                output.writeStartElement("body");
179
+                output.writeStartElement("div");
180
+                output.writeAttribute("style", "float: left; padding-right: 10px");
181
+                output.writeStartElement("p");
182
+                output.writeTextElement("strong", TTR("batchDetails", "Roasted Coffee: "));
183
+                output.writeTextElement("span", table.data(row, 2));
184
+                output.writeEndElement();
185
+                output.writeEndElement();
186
+                output.writeStartElement("div");
187
+                output.writeAttribute("style", "float: left");
188
+                output.writeStartElement("p");
189
+                output.writeTextElement("strong", TTR("batchDetails", "Roasted On: "));
190
+                batchTime = table.data(row, 0);
191
+                output.writeTextElement("span", batchTime);
192
+                output.writeEndElement();
193
+                output.writeStartElement("p");
194
+                output.writeTextElement("strong", TTR("batchDetails", "Batch Duration: "));
195
+                output.writeTextElement("span", table.data(row, 6));
196
+                output.writeEndElement();
197
+                output.writeEndElement();
198
+                output.writeStartElement("div");
199
+                output.writeAttribute("style", "clear: both");
200
+                output.writeEndElement();
201
+                output.writeStartElement("table");
202
+                output.writeStartElement("thead");
203
+                output.writeStartElement("tr");
204
+                output.writeTextElement("th", TTR("batchDetails", "Green Coffee"));
205
+                output.writeTextElement("th", TTR("batchDetails", "Weight (") + unitText + ")");
206
+                output.writeEndElement();
207
+                output.writeEndElement();
208
+                output.writeStartElement("tbody");
209
+                var query = new QSqlQuery();
210
+                query.prepare("SELECT unroasted_id, unroasted_quantity, approval, files, annotation, (unroasted_total_quantity/:c1)::numeric(12,3), (roasted_quantity/:c2)::numeric(12,3) FROM roasting_log WHERE time = :time AND machine = :machine");
211
+                query.bind(":time", batchTime);
212
+                query.bind(":c1", conversion);
213
+                query.bind(":c2", conversion);
214
+                machine = table.data(row, 1);
215
+                query.bind(":machine", machine);
216
+                query.exec();
217
+                query.next();
218
+                approval = query.value(2);
219
+                annotation = query.value(4);
220
+                var items = sqlToArray(query.value(0));
221
+                var quantities = sqlToArray(query.value(1));
222
+                var nameQuery = new QSqlQuery();
223
+                nameQuery.prepare("SELECT name FROM items WHERE id = :id");
224
+                for(var i = 0; i < items.length; i++) {
225
+                    output.writeStartElement("tr");
226
+                    nameQuery.bind(":id", items[i]);
227
+                    nameQuery.exec();
228
+                    nameQuery.next();
229
+                    output.writeTextElement("td", nameQuery.value(0) + " (" + items[i] + ")");
230
+                    output.writeStartElement("td");
231
+                    output.writeAttribute("align", "center");
232
+                    output.writeCharacters((Number(quantities[i])/conversion).toFixed(3));
233
+                    output.writeEndElement();
234
+                    output.writeEndElement();
235
+                }
236
+                nameQuery = nameQuery.invalidate();
237
+                output.writeEndElement();
238
+                output.writeStartElement("tfoot");
239
+                output.writeStartElement("tr");
240
+                output.writeStartElement("td");
241
+                output.writeAttribute("align", "right");
242
+                output.writeTextElement("strong", TTR("batchDetails", "Green Total:"));
243
+                output.writeEndElement();
244
+                output.writeStartElement("td");
245
+                output.writeAttribute("align", "center");
246
+                output.writeCharacters(query.value(5));
247
+                output.writeEndElement();
248
+                output.writeEndElement();
249
+                output.writeStartElement("tr");
250
+                output.writeStartElement("td");
251
+                output.writeAttribute("align", "right");
252
+                output.writeTextElement("strong", TTR("batchDetails", "Roasted Weight:"));
253
+                output.writeEndElement();
254
+                output.writeStartElement("td");
255
+                output.writeAttribute("align", "center");
256
+                output.writeCharacters(query.value(6));
257
+                roastWeight = query.value(6);
258
+                output.writeEndElement();
259
+                output.writeEndElement();
260
+                output.writeStartElement("tr");
261
+                output.writeStartElement("td");
262
+                output.writeAttribute("align", "right");
263
+                output.writeTextElement("strong", TTR("batchDetails", "Weight Loss:"));
264
+                output.writeEndElement();
265
+                output.writeStartElement("td");
266
+                output.writeAttribute("align", "center");
267
+                output.writeCharacters(table.data(row, 5));
268
+                output.writeCharacters("%");
269
+                output.writeEndElement();
270
+                output.writeEndElement();
271
+                output.writeEndElement();
272
+                output.writeEndElement();
273
+                output.writeStartElement("p");
274
+                output.writeTextElement("strong", TTR("batchDetails", "Approved: "));
275
+                output.writeCharacters(approval);
276
+                output.writeEndElement();
277
+                output.writeStartElement("p");
278
+                output.writeTextElement("strong", TTR("batchDetails", "Files: "));
279
+                output.writeCharacters(query.value(3));
280
+                output.writeEndElement();
281
+                output.writeStartElement("p");
282
+                output.writeTextElement("strong", TTR("batchDetails", "Annotations:"));
283
+                var files = sqlToArray(query.value(3));
284
+                var annotations = annotationFromRecord(files[0]);
285
+                fileID = files[0];
286
+                var buffer2 = new QBuffer("<points>"+annotations+"</points>");
287
+                buffer2.open(1);
288
+                var colQuery = new XQuery;
289
+                colQuery.bind("profile", buffer2);
290
+                colQuery.setQuery('for $i in doc($profile)//tuple[1]/temperature/@series return (string($i), ";")');
291
+                var result = colQuery.exec();
292
+                buffer2.close();
293
+                var seriesHeaders = new Array();
294
+                seriesHeaders.push(TTR("batchDetails", "Time"));
295
+                var records = result.split(";");
296
+                for(var i = 0; i < records.length - 1; i++) {
297
+                    seriesHeaders.push(records[i].replace(/^\s+|\s+$/g,""));
298
+                }
299
+                seriesHeaders.push(TTR("batchDetails", "Note"));
300
+                output.writeStartElement("table");
301
+                output.writeStartElement("thead");
302
+                output.writeStartElement("tr");
303
+                for(var i = 0; i < seriesHeaders.length; i++) {
304
+                        output.writeTextElement("th", seriesHeaders[i]);
305
+                }
306
+                output.writeEndElement();
307
+                output.writeEndElement();
308
+                buffer2.open(1);
309
+                var rq = 'for $t in doc($profile) //tuple return (string($t/time), ";", ';
310
+                for(var i = 0; i < seriesHeaders.length - 2; i++) {
311
+                        rq = rq + 'string($t/temperature[' + Number(i+1) + ']), ";", ';
312
+                }
313
+                rq = rq + 'string($t/annotation), "~")';
314
+                colQuery.setQuery(rq);
315
+                var annotationData = colQuery.exec();
316
+                colQuery = colQuery.invalidate();
317
+                buffer2.close();
318
+                output.writeStartElement("tbody");
319
+                var annotationRecords = annotationData.split("~");
320
+                for(var i = 0; i < annotationRecords.length - 1; i++) {
321
+                    output.writeStartElement("tr");
322
+                    var annotationRow = annotationRecords[i].split(";");
323
+                    for(var j = 0; j < annotationRow.length; j++) {
324
+                        output.writeStartElement("td");
325
+                        output.writeAttribute("style", "border-left: 1px solid #000000");
326
+                        if(j > 0) {
327
+                            output.writeAttribute("align", "center");
328
+                        }
329
+                        if(j > 0 && j < annotationRow.length - 1) {
330
+                            output.writeCharacters(Number(annotationRow[j].replace(/^\s+|\s+$/g,"")).toFixed(2));
331
+                        }
332
+                        else {
333
+                            output.writeCharacters(annotationRow[j].replace(/^\s+|\s+$/g,""));
334
+                        }
335
+                        output.writeEndElement();
336
+                    }
337
+                    output.writeEndElement();
338
+                }
339
+                output.writeEndElement();
340
+                output.writeEndElement();
341
+                output.writeCharacters(annotation);
342
+                output.writeEndElement();
343
+                output.writeEndElement();
344
+                output.writeEndElement();
345
+                output.writeEndDocument();
346
+                dataView.setContent(buffer);
347
+                buffer.close();
348
+                query = query.invalidate();
349
+                edit.enabled = true;
350
+            };
351
+            var saveMenu = findChildObject(this, 'save');
352
+            saveMenu.triggered.connect(function() {
353
+                var filename = QFileDialog.getSaveFileName(window, TTR("batchDetails", "Save Log As..."), QSettings.value("script/lastDir", "") + "/");
354
+                if(filename != "") {
355
+                    saveFileFromDatabase(fileID, filename);
356
+                }
357
+            });
347
         ]]>
358
         ]]>
348
     </program>
359
     </program>
349
 </window>
360
 </window>

+ 56
- 40
config/Windows/editbatchdetails.xml View File

1
 <window id="editBatchDetails">
1
 <window id="editBatchDetails">
2
     <layout type="vertical">
2
     <layout type="vertical">
3
-		<button type="check" name="Approved" id="approval" />
4
-		<layout type="horizontal">
5
-			<label>Annotation</label>
6
-			<textarea id="annotation" />
7
-		</layout>
8
-		<button type="push" id="submit" name="Submit" />
9
-	</layout>
3
+        <layout type="horizontal">
4
+            <label>Roasted Weight: </label>
5
+            <line id="roasted" validator="numeric" />
6
+            <line id="roastunit" writable="false" />
7
+        </layout>
8
+        <button type="check" name="Approved" id="approval" />
9
+        <layout type="horizontal">
10
+            <label>Annotation</label>
11
+            <textarea id="annotation" />
12
+        </layout>
13
+        <button type="push" id="submit" name="Submit" />
14
+    </layout>
10
     <program>
15
     <program>
11
         <![CDATA[
16
         <![CDATA[
12
-			var window = this;
13
-			var approvalButton = findChildObject(this, 'approval');
14
-			var annotationField = findChildObject(this, 'annotation');
15
-			var parentWindow;
16
-			var tableReference;
17
-			var rowReference;
18
-			var timeKey;
19
-			var machineKey;
20
-			var submit = findChildObject(this, 'submit');
21
-			submit.enabled = false;
22
-			this.loadData = function(parent, table, row, time, machine, approval, annotation) {
23
-				parentWindow = parent;
24
-				tableReference = table;
25
-				rowReference = row;
26
-				if(approval == "true") {
27
-					approvalButton.checked = true;
28
-				}
29
-				annotationField.plainText = annotation;
30
-				timeKey = time;
31
-				machineKey = machine;
32
-				submit.enabled = true;
33
-			};
34
-			submit.clicked.connect(function() {
35
-				var query = new QSqlQuery;
36
-				query.prepare("UPDATE roasting_log SET approval = :approval, annotation = :annotation WHERE time = :time AND machine = :machine");
37
-				query.bind("approval", approvalButton.checked);
38
-				query.bind("annotation", annotationField.plainText);
39
-				query.bind("time", timeKey);
40
-				query.bind("machine", machineKey);
41
-				query.exec();
42
-				parentWindow.loadData(tableReference, rowReference);
43
-				window.close();
44
-			});
17
+            var window = this;
18
+            var approvalButton = findChildObject(this, 'approval');
19
+            var annotationField = findChildObject(this, 'annotation');
20
+            var parentWindow;
21
+            var tableReference;
22
+            var rowReference;
23
+            var timeKey;
24
+            var machineKey;
25
+            var submit = findChildObject(this, 'submit');
26
+            submit.enabled = false;
27
+            var roastedEdit = findChildObject(this, 'roasted');
28
+            var unitEdit = findChildObject(this, 'roastunit');
29
+            var conversion = 1;
30
+            this.loadData = function(parent, table, row, time, machine, approval, annotation, roastWeight, unit) {
31
+                parentWindow = parent;
32
+                tableReference = table;
33
+                rowReference = row;
34
+                if(approval == "true") {
35
+                    approvalButton.checked = true;
36
+                }
37
+                annotationField.plainText = annotation;
38
+                timeKey = time;
39
+                machineKey = machine;
40
+                submit.enabled = true;
41
+                roastedEdit.text = roastWeight;
42
+                if(unit == 0) {
43
+                    unitEdit.text = TTR("editBatchDetails", "Kg");
44
+                    conversion = 2.2;
45
+                } else {
46
+                    unitEdit.text = TTR("editBatchDetails", "Lb");
47
+                }
48
+            };
49
+            submit.clicked.connect(function() {
50
+                var query = new QSqlQuery;
51
+                query.prepare("UPDATE roasting_log SET roasted_quantity = :roasted, approval = :approval, annotation = :annotation WHERE time = :time AND machine = :machine");
52
+                query.bind(":approval", approvalButton.checked);
53
+                query.bind(":annotation", annotationField.plainText);
54
+                query.bind(":roasted", Number(roastedEdit.text)/conversion);
55
+                query.bind(":time", timeKey);
56
+                query.bind(":machine", Number(machineKey));
57
+                query.exec();
58
+                parentWindow.loadData(tableReference, rowReference);
59
+                window.close();
60
+            });
45
         ]]>
61
         ]]>
46
     </program>
62
     </program>
47
 </window>
63
 </window>

+ 6
- 4
config/Windows/editfee.xml View File

15
 	<program>
15
 	<program>
16
 		<![CDATA[
16
 		<![CDATA[
17
 			window = this;
17
 			window = this;
18
+                        var invoiceID = 0;
18
 			this.windowTitle = 'Typica - Fee Detail';
19
 			this.windowTitle = 'Typica - Fee Detail';
19
 			var descField = findChildObject(this, 'description');
20
 			var descField = findChildObject(this, 'description');
20
 			var costField = findChildObject(this, 'cost');
21
 			var costField = findChildObject(this, 'cost');
21
 			this.dataSet = function() {
22
 			this.dataSet = function() {
22
 				descField.text = window.rowData[2];
23
 				descField.text = window.rowData[2];
23
-				costField.text = window.rowData[4];
24
+				costField.text = window.rowData[7];
25
+                                invoiceID = window.invoiceID;
24
 			};
26
 			};
25
 			button = findChildObject(this, 'submit');
27
 			button = findChildObject(this, 'submit');
26
 			button.clicked.connect(function() {
28
 			button.clicked.connect(function() {
27
 				var query = new QSqlQuery();
29
 				var query = new QSqlQuery();
28
-				query.prepare("UPDATE invoice_items SET description = :name, cost = :cost WHERE invoice_id = :id AND record_type = 'FEE' AND item_id = NULL AND description = :oldname AND cost = :oldcost");
30
+				query.prepare("UPDATE invoice_items SET description = :name, cost = :cost WHERE invoice_id = :id AND record_type = 'FEE' AND description = :oldname AND cost = :oldcost");
29
 				query.bind(":name", descField.text);
31
 				query.bind(":name", descField.text);
30
 				query.bind(":cost", Number(costField.text));
32
 				query.bind(":cost", Number(costField.text));
31
-				query.bind(":id", Number(window.rowData[1]);
33
+				query.bind(":id", Number(invoiceID));
32
 				query.bind(":oldname", window.rowData[2]);
34
 				query.bind(":oldname", window.rowData[2]);
33
-				query.bind(":oldcost", window.rowData[4]);
35
+				query.bind(":oldcost", window.rowData[7]);
34
 				query.exec();
36
 				query.exec();
35
 				query = query.invalidate();
37
 				query = query.invalidate();
36
 				window.close();
38
 				window.close();

+ 210
- 0
config/Windows/editreminder.xml View File

1
+<window id="editreminder">
2
+    <layout type="vertical">
3
+        <layout type="horizontal">
4
+            <label>Name:</label>
5
+            <line id="name" />
6
+        </layout>
7
+        <label>Description:</label>
8
+        <textarea id="description" />
9
+        <layout type="horizontal">
10
+            <label>Since:</label>
11
+            <calendar time="true" id="since" />
12
+            <button name="Now" id="now" type="push" />
13
+        </layout>
14
+        <layout type="horizontal">
15
+            <label>Condition:</label>
16
+            <sqldrop id="condition" />
17
+        </layout>
18
+        <layout type="stack" id="conditionvars">
19
+            <page>
20
+                <layout type="horizontal">
21
+                    <line id="productionweight" validator="numeric" />
22
+                    <sqldrop id="productionunit" />
23
+                </layout>
24
+            </page>
25
+            <page>
26
+                <layout type="horizontal">
27
+                    <line id="days" validator="numeric" />
28
+                </layout>
29
+            </page>
30
+            <page>
31
+                <layout type="horizontal">
32
+                    <line id="batches" validator="numeric" />
33
+                </layout>
34
+            </page>
35
+            <page>
36
+                <layout type="horizontal">
37
+                    <line id="roastingtime" validator="numeric" />
38
+                </layout>
39
+            </page>
40
+        </layout>
41
+        <layout type="horizontal">
42
+            <button name="Delete" id="delete" type="push" />
43
+            <button name="Save" id="save" type="push" />
44
+        </layout>
45
+    </layout>
46
+    <program>
47
+        <![CDATA[
48
+            var window = this;
49
+            window.setRefreshFunction = function(callback) {
50
+                window.refreshCallback = callback;
51
+            }
52
+            window.currentReminder = new Object();
53
+            this.windowTitle = TTR("editreminder", "Typica - Edit Reminder");
54
+            var unitBox = findChildObject(this, 'productionunit');
55
+            unitBox.addItem(TTR("editreminder", "Kg"));
56
+            unitBox.addItem(TTR("editreminder", "Lb"));
57
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
58
+            var condition = findChildObject(this, 'condition');
59
+            condition.addItem(TTR("editreminder", "Roasted Coffee Production"));
60
+            condition.addItem(TTR("editreminder", "Days"));
61
+            condition.addItem(TTR("editreminder", "Batches Roasted"));
62
+            condition.addItem(TTR("editreminder", "Hours of Roasting Time"));
63
+            var conditionpage = findChildObject(this, 'conditionvars');
64
+            condition['currentIndexChanged(int)'].connect(function(c) {
65
+                conditionpage.setCurrentIndex(c);
66
+            });
67
+            var namefield = findChildObject(this, 'name');
68
+            var descfield = findChildObject(this, 'description');
69
+            var sincefield = findChildObject(this, 'since');
70
+            var prodweight = findChildObject(this, 'productionweight');
71
+            var daysfield = findChildObject(this, 'days');
72
+            var batchesfield = findChildObject(this, 'batches');
73
+            var hoursfield = findChildObject(this, 'roastingtime');
74
+            var time = new QTime();
75
+            var nowButton = findChildObject(this, 'now');
76
+            nowButton.clicked.connect(function() {
77
+                sincefield.setToCurrentTime();
78
+            });
79
+            var deleteButton = findChildObject(this, 'delete');
80
+            deleteButton.enabled = false;
81
+            deleteButton.clicked.connect(function() {
82
+                var query = new QSqlQuery;
83
+                query.prepare("DELETE FROM reminders WHERE id = :id");
84
+                query.bind("id", window.currentReminder.dbid);
85
+                query.exec();
86
+                query = query.invalidate();
87
+                window.refreshCallback();
88
+                window.close();
89
+            });
90
+            var saveButton = findChildObject(this, 'save');
91
+            saveButton.clicked.connect(function() {
92
+                r = new Object();
93
+                r.title = namefield.text;
94
+                r.description = descfield.plainText;
95
+                r.start_year = sincefield.year();
96
+                r.start_month = sincefield.month();
97
+                r.start_day = sincefield.day();
98
+                r.start_time = sincefield.time.toString("hh:mm:ss");
99
+                switch(condition.currentIndex)
100
+                {
101
+                    case 0:
102
+                        r.condition = "PRODUCTIONWEIGHT";
103
+                        r.value = Number(prodweight.text);
104
+                        if(unitBox.currentIndex == 0)
105
+                        {
106
+                            r.unit = "KG";
107
+                        }
108
+                        else
109
+                        {
110
+                            r.unit = "LB";
111
+                        }
112
+                        break;
113
+                    case 1:
114
+                        r.condition = "DAYS";
115
+                        r.value = daysfield.text;
116
+                        break;
117
+                    case 2:
118
+                        r.condition = "PRODUCTIONBATCHES";
119
+                        r.value = batchesfield.text;
120
+                        break;
121
+                    case 3:
122
+                        r.condition = "PRODUCTIONHOURS";
123
+                        r.value = hoursfield.text;
124
+                        break;
125
+                }
126
+                var query = new QSqlQuery;
127
+                if(window.currentReminder.hasOwnProperty("dbid"))
128
+                {
129
+                    query.prepare("UPDATE reminders SET reminder = :reminder WHERE id = :id");
130
+                    query.bind("reminder", JSON.stringify(r));
131
+                    query.bind("id", window.currentReminder.dbid);
132
+                }
133
+                else
134
+                {
135
+                    query.prepare("INSERT INTO reminders (id, reminder) VALUES(default, :reminder)");
136
+                    query.bind("reminder", JSON.stringify(r));
137
+                }
138
+                query.exec();
139
+                query = query.invalidate();
140
+                window.refreshCallback();
141
+                window.close();
142
+            });
143
+            window.loadData = function(reminder) {
144
+                window.currentReminder = reminder;
145
+                deleteButton.enabled = reminder.hasOwnProperty("dbid");
146
+                if(reminder.hasOwnProperty("title"))
147
+                {
148
+                    namefield.text = reminder.title;
149
+                }
150
+                if(reminder.hasOwnProperty("description"))
151
+                {
152
+                    descfield.plainText = reminder.description;
153
+                }
154
+                if(reminder.hasOwnProperty("start_year") &&
155
+                   reminder.hasOwnProperty("start_month") &&
156
+                   reminder.hasOwnProperty("start_day"))
157
+                {
158
+                    sincefield.setDate(reminder.start_year, reminder.start_month, reminder.start_day);
159
+                }
160
+                if(reminder.hasOwnProperty("start_time"))
161
+                {
162
+                    sincefield.time = time.fromString(reminder.start_time, "hh:mm:ss");
163
+                }
164
+                if(reminder.hasOwnProperty("condition"))
165
+                {
166
+                    if(reminder.condition == "PRODUCTIONWEIGHT")
167
+                    {
168
+                        condition.setCurrentIndex(0);
169
+                        if(reminder.hasOwnProperty("value"))
170
+                        {
171
+                            prodweight.text = reminder.value;
172
+                        }
173
+                        if(reminder.unit == "KG")
174
+                        {
175
+                            unitBox.setCurrentIndex(0);
176
+                        }
177
+                        else
178
+                        {
179
+                            unitBox.setCurrentIndex(1);
180
+                        }
181
+                    }
182
+                    else if(reminder.condition == "DAYS")
183
+                    {
184
+                        condition.setCurrentIndex(1);
185
+                        if(reminder.hasOwnProperty("value"))
186
+                        {
187
+                            daysfield.text = reminder.value;
188
+                        }
189
+                    }
190
+                    else if(reminder.condition == "PRODUCTIONBATCHES")
191
+                    {
192
+                        condition.setCurrentIndex(2);
193
+                        if(reminder.hasOwnProperty("value"))
194
+                        {
195
+                            batchesfield.text = reminder.value;
196
+                        }
197
+                    }
198
+                    else if(reminder.condition == "PRODUCTIONHOURS")
199
+                    {
200
+                        condition.setCurrentIndex(3);
201
+                        if(reminder.hasOwnProperty("value"))
202
+                        {
203
+                            hoursfield.text = reminder.value;
204
+                        }
205
+                    }
206
+                }
207
+            }
208
+        ]]>
209
+    </program>
210
+</window>

+ 6
- 6
config/Windows/export.xml View File

72
             var titleField = findChildObject(window, 'title');
72
             var titleField = findChildObject(window, 'title');
73
             var subTitleField = findChildObject(window, 'subtitle');
73
             var subTitleField = findChildObject(window, 'subtitle');
74
             exportButton.clicked.connect(function() {
74
             exportButton.clicked.connect(function() {
75
-                var filename = QFileDialog.getSaveFileName(window, "Export XHTML+SVG As...", QSettings.value("script/lastDir", "") + "/");
75
+                var filename = QFileDialog.getSaveFileName(window, TTR("exportWindow", "Export XHTML+SVG As..."), QSettings.value("script/lastDir", "") + "/");
76
                 if(filename != "")
76
                 if(filename != "")
77
                 {
77
                 {
78
                     window.log.clearOutputColumns();
78
                     window.log.clearOutputColumns();
284
                     output.writeAttribute("transform", "rotate(-90 40,150)");
284
                     output.writeAttribute("transform", "rotate(-90 40,150)");
285
                     if(log.displayUnits() == 10143)
285
                     if(log.displayUnits() == 10143)
286
                     {
286
                     {
287
-                        output.writeCDATA("Temperature (°C)");
287
+                        output.writeCDATA(TTR("exportWindow", "Temperature (°C)"));
288
                     }
288
                     }
289
                     else
289
                     else
290
                     {
290
                     {
291
-                        output.writeCDATA("Temperature (°F)");
291
+                        output.writeCDATA(TTR("exportWindow", "Temperature (°F)"));
292
                     }
292
                     }
293
                     output.writeEndElement();
293
                     output.writeEndElement();
294
                     output.writeStartElement("text");
294
                     output.writeStartElement("text");
679
                     if(lossField.text != "")
679
                     if(lossField.text != "")
680
                     {
680
                     {
681
                         output.writeStartElement("p");
681
                         output.writeStartElement("p");
682
-                        output.writeCDATA("Weight loss: ");
682
+                        output.writeCDATA(TTR("exportWindow", "Weight loss: "));
683
                         output.writeCDATA(lossField.text);
683
                         output.writeCDATA(lossField.text);
684
                         if(tolField.text != "")
684
                         if(tolField.text != "")
685
                         {
685
                         {
691
                         output.writeStartElement("table");
691
                         output.writeStartElement("table");
692
                         output.writeStartElement("tr");
692
                         output.writeStartElement("tr");
693
                         output.writeStartElement("th");
693
                         output.writeStartElement("th");
694
-                        output.writeCDATA("Roasted");
694
+                        output.writeCDATA(TTR("exportWindow", "Roasted"));
695
                         output.writeEndElement();
695
                         output.writeEndElement();
696
                         output.writeStartElement("th");
696
                         output.writeStartElement("th");
697
-                        output.writeCDATA("Green");
697
+                        output.writeCDATA(TTR("exportWindow", "Green"));
698
                         output.writeEndElement();
698
                         output.writeEndElement();
699
                         output.writeEndElement();
699
                         output.writeEndElement();
700
                         var green;
700
                         var green;

+ 0
- 276
config/Windows/externalroaster.xml View File

1
-<window id="basicWindow">
2
-    <layout type="vertical">
3
-        <splitter type = "vertical" id = "main">
4
-            <splitter type = "horizontal" id = "indicators">
5
-                <decoration name="Bean Temperature" type="vertical">
6
-                    <lcdtemperature id = "beans" />
7
-                </decoration>
8
-                <decoration name="Air Temperature" type="vertical">
9
-                    <lcdtemperature id = "environment" />
10
-                </decoration>
11
-                <decoration name="Batch Timer" type="vertical">
12
-                    <lcdtimer format="mm:ss" id="batch" />
13
-                </decoration>
14
-            </splitter>
15
-            <widget id="widget">
16
-                <layout type="horizontal">
17
-                    <button name="Start Batch" type="push" id="startbutton" />
18
-                    <button name="Stop Batch" type="annotation" id="stopbutton" series="1" column="3" annotation="End" />
19
-                    <spinbox id="manometer" series="1" column="3" min="0" max="10" step="0.1" decimals="1" />
20
-                    <button name="New Sample" type="annotation" id="sample" series="1" column="3" annotation="Sample %1" />
21
-                </layout>
22
-            </widget>
23
-            <splitter type="horizontal" id="logsplit">
24
-                <measurementtable id="log">
25
-                    <column>Time</column>
26
-                    <column>Bean</column>
27
-                    <column>Air</column>
28
-                    <column>Note</column>
29
-                    <column>.</column>
30
-                    <column>.</column>
31
-                    <column>.</column>
32
-                </measurementtable>
33
-                <graph id="graph" />
34
-            </splitter>
35
-        </splitter>
36
-    </layout>
37
-    <menu name="File">
38
-        <item id="open" shortcut="Ctrl+O">Open…</item>
39
-        <item id="save" shortcut="Ctrl+S">Save…</item>
40
-        <item id="export">Export CSV…</item>
41
-        <item id="quit" shortcut="Ctrl+Q">Quit</item>
42
-    </menu>
43
-    <menu name="Batch">
44
-        <item id="new" shortcut="Ctrl+N">New Batch…</item>
45
-    </menu>
46
-    <menu name="Log">
47
-        <item id="clear" shortcut="Ctrl+L">Clear Log</item>
48
-        <separator />
49
-        <item id="ms">Millisecond View</item>
50
-        <item id="1s">1 Second View</item>
51
-        <item id="5s">5 Second View</item>
52
-        <item id="10s">10 Second View</item>
53
-        <item id="15s">15 Second View</item>
54
-        <item id="30s">30 Second View</item>
55
-        <item id="1m">1 Minute View</item>
56
-        <separator />
57
-        <item id="manual" shortcut="Ctrl+E">Manual Entry</item>
58
-    </menu>
59
-    <program>
60
-        lc = 1;
61
-        this.restoreSizeAndPosition('window');
62
-        var vsplit = findChildObject(this, 'main');
63
-        vsplit.restoreState("script/mainSplitter");
64
-        var isplit = findChildObject(this, 'indicators');
65
-        isplit.restoreState("script/instrumentSplitter");
66
-        var lsplit = findChildObject(this, 'logsplit');
67
-        lsplit.restoreState("script/logSplitter");
68
-        var log = findChildObject(this, 'log');
69
-        this.show();
70
-        log.restoreState("script/log", 7);
71
-        var window = this;
72
-        this.aboutToClose.connect(function() {
73
-            window.saveSizeAndPosition("window");
74
-            vsplit.saveState("script/mainSplitter");
75
-            isplit.saveState("script/instrumentSplitter");
76
-            lsplit.saveState("script/logSplitter");
77
-            log.saveState("script/log", 7);
78
-            loggingWindow = undefined;
79
-        });
80
-        vsplit.restoreState("script/mainSplitter");
81
-        isplit.restoreState("script/instrumentSplitter");
82
-        lsplit.restoreState("script/logSplitter");
83
-        var device = new DAQ('Dev1');
84
-        var bchannel = device.newChannel(DAQ.Fahrenheit, DAQ.TypeJ);
85
-        var achannel = device.newChannel(DAQ.Fahrenheit, DAQ.TypeJ);
86
-        device.setClockRate(2.0);
87
-        device.start();
88
-        beanDisplay = findChildObject(this, 'beans');
89
-        envDisplay = findChildObject(this, 'environment');
90
-        bchannel.newData.connect(beanDisplay.setValue);
91
-        achannel.newData.connect(envDisplay.setValue);
92
-        var epoch = new QTime;
93
-        epoch = epoch.currentTime;
94
-        var boffset = new MeasurementTimeOffset(epoch);
95
-        var aoffset = new MeasurementTimeOffset(epoch);
96
-        badapt = new MeasurementAdapter(1);
97
-        aadapt = new MeasurementAdapter(2);
98
-        bzero = new ZeroEmitter(1);
99
-        azero = new ZeroEmitter(2);
100
-        bchannel.newData.connect(boffset.newMeasurement);
101
-        achannel.newData.connect(aoffset.newMeasurement);
102
-        boffset.measurement.connect(badapt.newMeasurement);
103
-        aoffset.measurement.connect(aadapt.newMeasurement);
104
-        bchannel.newData.connect(bzero.newMeasurement);
105
-        achannel.newData.connect(azero.newMeasurement);
106
-        var graph = findChildObject(this, 'graph');
107
-        bzero.measurement.connect(log.newMeasurement);
108
-        bzero.measurement.connect(graph.newMeasurement);
109
-        azero.measurement.connect(log.newMeasurement);
110
-        azero.measurement.connect(graph.newMeasurement);
111
-        var timer = findChildObject(this, 'batch');
112
-        timer.autoReset = true;
113
-        var start = findChildObject(this, 'startbutton');
114
-        start.clicked.connect(function() {
115
-            var epoch = new QTime();
116
-            epoch = epoch.currentTime();
117
-            timer.startTimer();
118
-            boffset.setZeroTime(epoch);
119
-            aoffset.setZeroTime(epoch);
120
-            bzero.emitZero();
121
-            azero.emitZero();
122
-            aadapt.measurement.connect(log.newMeasurement);
123
-            aadapt.measurement.connect(graph.newMeasurement);
124
-            badapt.measurement.connect(log.newMeasurement);
125
-            badapt.measurement.connect(graph.newMeasurement);
126
-            if(typeof(currentBatchInfo) == 'undefined') { } else {
127
-                var query = new QSqlQuery();
128
-                query.exec("SELECT now()::timestamp without time zone");
129
-                query.next();
130
-                var result = query.value(0);
131
-                var timefield = findChildObject(currentBatchInfo, 'time');
132
-                timefield.text = result.replace('T', ' ');
133
-            }
134
-        });
135
-        start.setFocus();
136
-        var spin = findChildObject(this, 'manometer');
137
-        spin.annotation.connect(log.newAnnotation);
138
-        var stop = findChildObject(this, 'stopbutton');
139
-        stop.annotation.connect(log.newAnnotation);
140
-        stop.clicked.connect(timer.stopTimer);
141
-        stop.clicked.connect(function() {
142
-            badapt.measurement.disconnect(log.newMeasurement);
143
-            badapt.measurement.disconnect(graph.newMeasurement);
144
-            aadapt.measurement.disconnect(log.newMeasurement);
145
-            aadapt.measurement.disconnect(graph.newMeasurement);
146
-            spin.resetChange();
147
-            if(typeof(currentBatchInfo) == 'undefined') { } else {
148
-                var duration = log.lastTime(lc);
149
-                var durfield = findChildObject(currentBatchInfo, 'duration');
150
-                durfield.text = duration;
151
-                log.clearOutputColumns();
152
-                log.addOutputTemperatureColumn(lc);
153
-                log.addOutputTemperatureColumn(lc + 1);
154
-                log.addOutputAnnotationColumn(lc + 2);
155
-                var filename = log.saveTemporary();
156
-                currentBatchInfo.tempData = filename;
157
-                currentBatchInfo.raise();
158
-                currentBatchInfo.activateWindow();
159
-            }
160
-        });
161
-        var sample = findChildObject(this, 'sample');
162
-        sample.annotation.connect(log.newAnnotation);
163
-        var openMenu = findChildObject(this, 'open');
164
-        var window = this;
165
-        openMenu.triggered.connect(function() {
166
-            var filename = QFileDialog.getOpenFileName(window, 'Open Log…', QSettings.value('script/lastDir', '') + '/');
167
-            if(filename != '') {
168
-                var file = new QFile(filename);
169
-                var input = new XMLInput(file, 1);
170
-                input.newTemperatureColumn.connect(log.setHeaderData);
171
-                input.newAnnotationColumn.connect(log.setHeaderData);
172
-                input.measure.connect(graph.newMeasurement);
173
-                input.measure.connect(log.newMeasurement);
174
-                input.annotation.connect(log.newAnnotation);
175
-                input.lastColumn.connect(function(c) {
176
-                    lc = c + 1;
177
-                    badapt.setColumn(c + 1);
178
-                    aadapt.setColumn(c + 2);
179
-                    bzero.setColumn(c + 1);
180
-                    azero.setColumn(c + 2);
181
-                    stop.setTemperatureColumn(c + 1);
182
-                    stop.setAnnotationColumn(c + 3);
183
-                    sample.setTemperatureColumn(c + 1);
184
-                    sample.setAnnotationColumn(c + 3);
185
-                    spin.setTemperatureColumn(c + 1);
186
-                    spin.setAnnotationColumn(c + 3);
187
-                    log.setHeaderData(c + 1, "Bean");
188
-                    log.setHeaderData(c + 2, "Air");
189
-                    log.setHeaderData(c + 3, "Note");
190
-                });
191
-                input.input();
192
-                window.windowTitle = 'Typica - ' + baseName(filename);
193
-                QSettings.setValue("script/lastDir", dir(filename));
194
-            }
195
-        });
196
-        var quitMenu = findChildObject(this, 'quit');
197
-        quitMenu.triggered.connect(function() {
198
-            window.close();
199
-            Application.quit();
200
-        });
201
-        var saveMenu = findChildObject(this, 'save');
202
-        saveMenu.triggered.connect(function() {
203
-            var filename = QFileDialog.getSaveFileName(window, "Save Log As…", QSettings.value("script/lastDir", "") + "/");
204
-            if(filename != "") {
205
-                var file = new QFile(filename);
206
-                log.clearOutputColumns();
207
-                log.addOutputTemperatureColumn(lc);
208
-                log.addOutputTemperatureColumn(lc + 1);
209
-                log.addOutputAnnotationColumn(lc + 2);
210
-                log.saveXML(file);
211
-                QSettings.setValue("script/lastDir", dir(filename));
212
-            }
213
-        });
214
-        var exportMenu = findChildObject(this, 'export');
215
-        exportMenu.triggered.connect(function() {
216
-            var filename = QFileDialog.getSaveFileName(window, "Export CSV As…", QSettings.value("script/lastDir", "") + "/");
217
-            if(filename != "") {
218
-                var file = new QFile(filename);
219
-                log.clearOutputColumns();
220
-                log.addOutputTemperatureColumn(lc);
221
-                log.addOutputTemperatureColumn(lc + 1);
222
-                log.addOutputAnnotationColumn(lc + 2);
223
-                log.saveCSV(file);
224
-                QSettings.setValue("script/lastDir", dir(filename));
225
-            }
226
-        });
227
-        var clear = findChildObject(this, 'clear');
228
-        clear.triggered.connect(log.clear);
229
-        clear.triggered.connect(graph.clear);
230
-        clear.triggered.connect(function() {
231
-            window.windowTitle = "Typica";
232
-            log.setHeaderData(0, "Time");
233
-            log.setHeaderData(1, "Bean");
234
-            log.setHeaderData(2, "Air");
235
-            log.setHeaderData(3, "Note");
236
-            log.setHeaderData(4, "");
237
-            log.setHeaderData(5, "");
238
-            log.setHeaderData(6, "");
239
-            lc = 1;
240
-            badapt.setColumn(1);
241
-            aadapt.setColumn(2);
242
-            bzero.setColumn(1);
243
-            azero.setColumn(2);
244
-            stop.setTemperatureColumn(1);
245
-            stop.setAnnotationColumn(3);
246
-            sample.setTemperatureColumn(1);
247
-            sample.setAnnotationColumn(3);
248
-            spin.setTemperatureColumn(1);
249
-            spin.setAnnotationColumn(3);
250
-        });
251
-        var v1 = findChildObject(this, 'ms');
252
-        v1.triggered.connect(log.LOD_ms);
253
-        var v2 = findChildObject(this, '1s');
254
-        v2.triggered.connect(log.LOD_1s);
255
-        var v3 = findChildObject(this, '5s');
256
-        v3.triggered.connect(log.LOD_5s);
257
-        var v4 = findChildObject(this, '10s');
258
-        v4.triggered.connect(log.LOD_10s);
259
-        var v5 = findChildObject(this, '15s');
260
-        v5.triggered.connect(log.LOD_15s);
261
-        var v6 = findChildObject(this, '30s');
262
-        v6.triggered.connect(log.LOD_30s);
263
-        var v7 = findChildObject(this, '1m');
264
-        v7.triggered.connect(log.LOD_1m);
265
-        var manual = findChildObject(this, 'manual');
266
-        manual.triggered.connect(function() {
267
-            var entry = new LogEditWindow();
268
-            entry.show();
269
-        });
270
-        var newMenu = findChildObject(this, 'new');
271
-        newMenu.triggered.connect(function() {
272
-            var bwindow = createWindow("batchWindow");
273
-            bwindow.windowTitle = "Typica - New Batch";
274
-        });
275
-    </program>
276
-</window>

+ 76
- 89
config/Windows/greeninventory.xml View File

1
 <window id="inventory">
1
 <window id="inventory">
2
     <layout type="vertical">
2
     <layout type="vertical">
3
+        <layout type="horizontal">
4
+            <label>Transaction type: </label>
5
+            <sqldrop id="transactiontype" />
6
+            <stretch />
7
+        </layout>
3
         <layout type="horizontal">
8
         <layout type="horizontal">
4
             <sqldrop data="0" display="1" showdata="true" id="item">
9
             <sqldrop data="0" display="1" showdata="true" id="item">
5
                 <query><![CDATA[SELECT id, name FROM coffees WHERE quantity <> 0 ORDER BY name ASC]]></query>
10
                 <query><![CDATA[SELECT id, name FROM coffees WHERE quantity <> 0 ORDER BY name ASC]]></query>
6
             </sqldrop>
11
             </sqldrop>
7
             <line id="quantity" />
12
             <line id="quantity" />
8
             <sqldrop id="units" />
13
             <sqldrop id="units" />
14
+            <layout type="stack" id="optional">
15
+                <page />
16
+                <page>    
17
+                    <layout type="horizontal">
18
+                        <label id="reasonlabel">Reason: </label>
19
+                        <line id="reason" />
20
+                    </layout>
21
+                </page>
22
+            </layout>
23
+        </layout>
24
+        <layout type="horizontal">
9
             <button type="push" name="Update" id="update" />
25
             <button type="push" name="Update" id="update" />
26
+            <stretch />
10
         </layout>
27
         </layout>
11
         <textarea id="current" />
28
         <textarea id="current" />
12
     </layout>
29
     </layout>
13
     <program>
30
     <program>
14
         <![CDATA[
31
         <![CDATA[
15
-			var units = findChildObject(this, 'units');
16
-			units.addItem("bag");
17
-			units.addItem("lb");
18
-			units.addItem("kg");
19
-			var items = findChildObject(this, 'item');
20
-			var status = findChildObject(this, 'current');
21
-			var q = "SELECT quantity FROM items WHERE id = ";
22
-			q = q + items.currentData();
23
-			query = new QSqlQuery();
24
-			query.exec(q);
25
-			query.next();
26
-			var text = items.currentText;
27
-			text = text + " Current inventory: ";
28
-			text = text + query.value(0);
29
-			text = text + " pounds ("
30
-			text = text + Number(query.value(0)) / 2.2;
31
-			text = text + " Kg (";
32
-			q = "SELECT ";
33
-			q = q + query.value(0);
34
-			q = q + " / (SELECT conversion FROM lb_bag_conversion WHERE item = ";
35
-			q = q + items.currentData();
36
-			q = q + ")";
37
-			query.exec(q);
38
-			query.next();
39
-			text = text + query.value(0);
40
-			text = text + " bags)";
41
-            query = query.invalidate();
42
-			status.plainText = text;
43
-			var button = findChildObject(this, 'update');
44
-			var value = findChildObject(this, 'quantity');
45
-			button.clicked.connect(function() {
46
-				q = "INSERT INTO inventory (time, item, quantity) VALUES ('now', ";
47
-				q = q + items.currentData();
48
-				q = q + ", ";
49
-				if(units.currentText == "lb") {
50
-					q = q + value.text;
51
-				} else if (units.currentText == "kg") {
52
-					q = q + (value.text * 2.2);
53
-				}
54
-				else {
55
-					q = q + value.text;
56
-					q = q + " * (SELECT conversion FROM lb_bag_conversion WHERE item = ";
57
-					q = q + items.currentData();
58
-					q = q + ")";
59
-				}
60
-				q = q + ")";
32
+            var types = findChildObject(this, 'transactiontype');
33
+            types.addItem(TTR("inventory", "inventory"));
34
+            types.addItem(TTR("inventory", "loss"));
35
+            var optionalDisplay = findChildObject(this, 'optional');
36
+            var units = findChildObject(this, 'units');
37
+            units.addItem(TTR("inventory", "bag"));
38
+            units.addItem(TTR("inventory", "Lb"));
39
+            units.addItem(TTR("inventory", "Kg"));
40
+            var items = findChildObject(this, 'item');
41
+            var status = findChildObject(this, 'current');
42
+            function updateStatus() {
61
                 query = new QSqlQuery();
43
                 query = new QSqlQuery();
62
-				query.exec(q);
63
-				text = items.currentText;
64
-				q = "SELECT quantity FROM items WHERE id = ";
65
-				q = q + items.currentData();
66
-				query.exec(q);
67
-				query.next();
68
-				text = text + " Current inventory: ";
69
-				text = text + query.value(0);
70
-				text = text + " pounds (";
71
-				q = "SELECT ";
72
-				q = q + query.value(0);
73
-				q = q + " / (SELECT conversion FROM lb_bag_conversion WHERE item = ";
74
-				q = q + items.currentData();
75
-				q = q + ")";
76
-				query.exec(q);
77
-				query.next();
78
-				text = text + query.value(0);
79
-				text = text + " bags)";
80
-				status.plainText = text;
44
+                query.prepare("SELECT quantity, (quantity / 2.2)::numeric(12,3), (quantity / (SELECT conversion FROM lb_bag_conversion WHERE item = :id1))::numeric(12,2) FROM items WHERE id = :id2");
45
+                query.bind(":id1", items.currentData());
46
+                query.bind(":id2", items.currentData());
47
+                query.exec();
48
+                query.next();
49
+                var text = items.currentText;
50
+                text += " Current inventory: ";
51
+                text += query.value(0);
52
+                text += TTR("inventory", " Lb (");
53
+                text += query.value(1);
54
+                text += TTR("inventory", " Kg), ");
55
+                text += query.value(2);
56
+                text += (query.value(2) == "1" ? TTR("inventory", " bag") :
57
+                    TTR("inventory", " bags"));
81
                 query = query.invalidate();
58
                 query = query.invalidate();
82
-			});
83
-			items['currentIndexChanged(int)'].connect(function() {
84
-				q = "SELECT quantity FROM items WHERE id = ";
85
-				q = q + items.currentData();
59
+                status.plainText = text;
60
+            }
61
+            var button = findChildObject(this, 'update');
62
+            var value = findChildObject(this, 'quantity');
63
+            var reason = findChildObject(this, 'reason');
64
+            var reasonlabel = findChildObject(this, 'reasonlabel');
65
+            button.clicked.connect(function() {
66
+                q = "INSERT INTO ";
67
+                q += (types.currentIndex == 0 ?
68
+                    "inventory (time, item, quantity)" :
69
+                    "loss (time, item, quantity, reason)");
70
+                q += " VALUES ('now', ";
71
+                q = q + items.currentData();
72
+                q = q + ", ";
73
+                if(units.currentText == TTR("inventory", "Lb")) {
74
+                    q = q + value.text;
75
+                } else if (units.currentText == TTR("inventory", "Kg")) {
76
+                    q = q + (value.text * 2.2);
77
+                }
78
+                else {
79
+                    q = q + value.text;
80
+                    q = q + " * (SELECT conversion FROM lb_bag_conversion WHERE item = ";
81
+                    q = q + items.currentData();
82
+                    q = q + ")";
83
+                }
84
+                q += (types.currentIndex == 0 ?
85
+                    ")" :
86
+                    ", '" + reason.text + "')");
86
                 query = new QSqlQuery();
87
                 query = new QSqlQuery();
87
-				query.exec(q);
88
-				query.next();
89
-				var text = items.currentText;
90
-				text = text + " Current inventory: ";
91
-				text = text + query.value(0);
92
-				text = text + " pounds ";
93
-				text = text + Number(query.value(0)) / 2.2;
94
-				text = text + " Kg (";
95
-				q = "SELECT ";
96
-				q = q + query.value(0);
97
-				q = q + " / (SELECT conversion FROM lb_bag_conversion WHERE item = ";
98
-				q = q + items.currentData();
99
-				q = q + ")";
100
-				query.exec(q);
101
-				query.next();
102
-				text = text + query.value(0);
103
-				text = text + " bags)";
104
-				status.plainText = text;
105
-                query = query.invalidate();
106
-			});
88
+                query.exec(q);
89
+                updateStatus();
90
+            });
91
+            items['currentIndexChanged(int)'].connect(updateStatus);
92
+            types['currentIndexChanged(int)'].connect(optionalDisplay.setCurrentIndex);
93
+            updateStatus();
107
         ]]>
94
         ]]>
108
     </program>
95
     </program>
109
 </window>
96
 </window>

+ 3
- 3
config/Windows/greensales.xml View File

21
 	<program>
21
 	<program>
22
 		<![CDATA[
22
 		<![CDATA[
23
 			var window = this;
23
 			var window = this;
24
-			this.windowTitle = 'Typica - Enter Green Coffee Sales';
24
+			this.windowTitle = TTR("greensales", "Typica - Enter Green Coffee Sales");
25
 			var unitBox = findChildObject(this, 'units');
25
 			var unitBox = findChildObject(this, 'units');
26
 			unitBox.addItem("g");
26
 			unitBox.addItem("g");
27
 			unitBox.addItem("Kg");
27
 			unitBox.addItem("Kg");
28
 			unitBox.addItem("oz");
28
 			unitBox.addItem("oz");
29
-			unitBox.addItem("lb");
30
-			unitBox.currentIndex = (QSettings.value("script/greensales_unit", unitBox.findText("lb")));
29
+			unitBox.addItem("Lb");
30
+			unitBox.currentIndex = (QSettings.value("script/greensales_unit", unitBox.findText("Lb")));
31
 			unitBox['currentIndexChanged(int)'].connect(function() {
31
 			unitBox['currentIndexChanged(int)'].connect(function() {
32
 				QSettings.setValue("script/greensales_unit", unitBox.currentIndex);
32
 				QSettings.setValue("script/greensales_unit", unitBox.currentIndex);
33
 			});
33
 			});

+ 0
- 47
config/Windows/history.xml View File

1
-<window id="history">
2
-    <layout type="vertical">
3
-		<layout type="horizontal">
4
-			<daterange id="dates" initial="6" /><!-- Last 7 Days -->
5
-			<stretch />
6
-		</layout>
7
-        <sqlview id="table" />
8
-    </layout>
9
-    <program>
10
-        <![CDATA[
11
-			var dateSelect = findChildObject(this, 'dates');
12
-			var dateQuery = new QSqlQuery();
13
-			dateQuery.exec("SELECT time::date FROM roasting_log WHERE time = (SELECT min(time) FROM roasting_log) OR time = (SELECT max(time) FROM roasting_log) ORDER BY time ASC");
14
-			dateQuery.next();
15
-			var lifetimeStartDate = dateQuery.value(0);
16
-			var lifetimeEndDate;
17
-			if(dateQuery.next()) {
18
-				lifetimeEndDate = dateQuery.value(0);
19
-			} else {
20
-				lifetimeEndDate = lifetimeStartDate;
21
-			}
22
-			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
23
-			dateQuery.invalidate();
24
-			dateSelect.currentIndex = QSettings.value("script/history/dateIndex", 6);
25
-			var table = findChildObject(this, 'table');
26
-			table.openEntryRow.connect(function(arg) {
27
-				var details = createWindow("batchDetails");
28
-				details.loadData(table, arg);
29
-			});
30
-			function refresh() {
31
-				var dateRange = dateSelect.currentRange();
32
-				var startDate = "'"+dateRange[0]+"'";
33
-				var endDate = "'"+dateRange[dateRange.length - 1]+"'";
34
-				var q = "SELECT time, machine, (SELECT name FROM items WHERE id = roasted_id) AS name, unroasted_total_quantity AS green, roasted_quantity AS roasted, CASE WHEN unroasted_total_quantity = 0 THEN NULL ELSE ((unroasted_total_quantity - roasted_quantity) / unroasted_total_quantity * 100::numeric)::numeric(12,2) END AS weight_loss, duration, annotation FROM roasting_log WHERE time >= " + startDate + "::date AND time < " + endDate + "::date + interval '1 day' ORDER BY time DESC";
35
-				table.setQuery(q);
36
-				table.hideColumn(1);
37
-			}
38
-			dateSelect.rangeUpdated.connect(function() {
39
-				if(dateSelect.currentIndex != 24) { // Custom date range
40
-					QSettings.setValue("script/history/dateIndex", dateSelect.currentIndex);
41
-				}
42
-				refresh();
43
-			});
44
-			refresh();
45
-        ]]>
46
-    </program>
47
-</window>

+ 1
- 1
config/Windows/importprofiles.xml View File

10
         var box = findChildObject(this, 'roasted');
10
         var box = findChildObject(this, 'roasted');
11
         var win = this;
11
         var win = this;
12
         button.clicked.connect(function() {
12
         button.clicked.connect(function() {
13
-            var filename = QFileDialog.getOpenFileName(win, 'Open Log…', QSettings.value('script/lastDir', '') + '/');
13
+            var filename = QFileDialog.getOpenFileName(win, TTR("importTargets", "Open Log..."), QSettings.value('script/lastDir', '') + '/');
14
             if(filename != '') {
14
             if(filename != '') {
15
                 QSettings.setValue("script/lastDir", dir(filename));
15
                 QSettings.setValue("script/lastDir", dir(filename));
16
                 var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
16
                 var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";

+ 4
- 1
config/Windows/invoiceinfo.xml View File

14
 	<program>
14
 	<program>
15
 		<![CDATA[
15
 		<![CDATA[
16
 			var window = this;
16
 			var window = this;
17
+                        var invoiceID = 0;
17
 			var table = findChildObject(this, 'itemtable');
18
 			var table = findChildObject(this, 'itemtable');
18
 			this.setInvoiceID = function(arg) {
19
 			this.setInvoiceID = function(arg) {
19
 				window.invoiceID = arg;
20
 				window.invoiceID = arg;
20
-				window.windowTitle = "Typica - Invoice Details " + arg;
21
+                                invoiceID = arg;
22
+				window.windowTitle = TTR("invoiceinfo", "Typica - Invoice Details ") + arg;
21
 			};
23
 			};
22
 			button = findChildObject(this, 'edit');
24
 			button = findChildObject(this, 'edit');
23
 			button.clicked.connect(function() {
25
 			button.clicked.connect(function() {
43
 					for(var i = 0; i < 8; i++) {
45
 					for(var i = 0; i < 8; i++) {
44
 						feeWindow.rowData[i] = table.data(arg, i);
46
 						feeWindow.rowData[i] = table.data(arg, i);
45
 					}
47
 					}
48
+                                        feeWindow.invoiceID = invoiceID;
46
 					feeWindow.dataSet();
49
 					feeWindow.dataSet();
47
 				}
50
 				}
48
 			});
51
 			});

+ 0
- 28
config/Windows/invoicelist.xml View File

1
-<window id="invoicelist">
2
-	<layout type="vertical">
3
-		<sqlview id="table" />
4
-	</layout>
5
-	<program>
6
-		<![CDATA[
7
-			this.windowTitle = "Typica - Invoice List";
8
-			var table = findChildObject(this, 'table');
9
-			table.setQuery("SELECT id, time, invoice, vendor, (SELECT sum(cost) FROM invoice_items WHERE invoice_id = id) AS cost FROM invoices ORDER BY time DESC");
10
-			table.openEntry.connect(function(arg) {
11
-				var info = createWindow("invoiceinfo");
12
-				info.setInvoiceID(arg);
13
-				var query = new QSqlQuery();
14
-				query.exec("SELECT time, invoice, vendor FROM invoices WHERE id = " + arg);
15
-				query.next();
16
-				var timefield = findChildObject(info, 'date');
17
-				timefield.text = query.value(0);
18
-				var vendorfield = findChildObject(info, 'vendor');
19
-				vendorfield.text = query.value(2);
20
-				var invoicefield = findChildObject(info, 'invoice');
21
-				invoicefield.text = query.value(1);
22
-				var itemtable = findChildObject(info, 'itemtable');
23
-				itemtable.setQuery("SELECT record_type, item_id, description, (SELECT reference FROM items WHERE id = item_id) AS reference, (SELECT cost FROM purchase WHERE item = item_id) AS unit_cost, (SELECT quantity FROM purchase WHERE item = item_id) AS quantity, ((SELECT quantity FROM purchase WHERE item = item_id)/(SELECT conversion FROM lb_bag_conversion WHERE item = item_id))::numeric(12,2) AS sacks, cost FROM invoice_items WHERE invoice_id = " + arg + " AND record_type = 'PURCHASE' UNION SELECT record_type, NULL, description, NULL, NULL, NULL, NULL, cost FROM invoice_items WHERE invoice_id = " + arg + " AND record_type = 'FEE' ORDER BY item_id");
24
-				query = query.invalidate();
25
-			});
26
-		]]>
27
-	</program>
28
-</window>

+ 202
- 198
config/Windows/navigation.xml View File

1
 <window id="navwindow">
1
 <window id="navwindow">
2
-	<layout type="grid">
3
-		<row>
4
-			<column>
5
-				<button name="Configure Roasters" id="configure" type="push" />
6
-			</column>
7
-		</row>
8
-		<row>
9
-			<column>
10
-				<sqldrop id="machineselector" />
11
-			</column>
12
-			<column>
13
-				<button name="Roast Coffee" id="roast" type="push" />
14
-			</column>
15
-		</row>
16
-		<row>
17
-			<column>
18
-				<button name="Purchase Green Coffee" id="green" type="push" />
19
-			</column>
20
-		</row>
21
-		<row>
22
-			<column>
23
-				<button name="Manage Roasted Coffee Items" id="newroasted" type="push" />
24
-			</column>
25
-		</row>
26
-		<row>
27
-			<column>
28
-				<button name="Update Inventory" id="inventory" type="push" />
29
-			</column>
30
-		</row>
31
-		<row>
32
-			<column>
33
-				<button name="Batch Log" id="history" type="push" />
34
-			</column>
35
-		</row>
36
-		<row>
37
-			<column>
38
-				<button name="New Cupping Session" id="createcupping"
39
-type="push" />
40
-			</column>
41
-		</row>
42
-		<row>
43
-			<column>
44
-				<button name="Join Cupping Session" id="joincupping" type="push"
45
-/>
46
-			</column>
47
-		</row>
48
-		<row>
49
-			<column>
50
-				<button name="Summarize Cupping Session" id="sumcupping"
51
-type="push" />
52
-			</column>
53
-		</row>
54
-		<row>
55
-			<column>
56
-				<button name="View Target Roast Profiles" id="profilehistory"
57
-					type="push" />
58
-			</column>
59
-		</row>
60
-		<row>
61
-			<column>
62
-				<button name="Import Target Roast Profiles" id="target"
63
-type="push" />
64
-			</column>
65
-		</row>
66
-		<row>
67
-			<column>
68
-				<button name="Invoice List" id="invoicelist" type="push" />
69
-			</column>
70
-		</row>
71
-		<row>
72
-			<column>
73
-				<button name="Enter Green Coffee Sales" id="greensales" type="push" />
74
-			</column>
75
-		</row>
76
-	</layout>
2
+    <layout type="grid">
3
+        <row>
4
+            <column>
5
+                <button name="Configure Roasters" id="configure" type="push" />
6
+            </column>
7
+        </row>
8
+        <row>
9
+            <column>
10
+                <sqldrop id="machineselector" />
11
+            </column>
12
+            <column>
13
+                <button name="Roast Coffee" id="roast" type="push" />
14
+            </column>
15
+        </row>
16
+        <row>
17
+            <column>
18
+                <button name="Purchase Green Coffee" id="green" type="push" />
19
+            </column>
20
+        </row>
21
+        <row>
22
+            <column>
23
+                <button name="Manage Roasted Coffee Items" id="newroasted" type="push" />
24
+            </column>
25
+        </row>
26
+        <row>
27
+            <column>
28
+                <button name="Edit Roasting Specification" id="roastspec" type="push" />
29
+            </column>
30
+        </row>
31
+        <row>
32
+            <column>
33
+                <button name="Update Inventory" id="inventory" type="push" />
34
+            </column>
35
+        </row>
36
+        <row>
37
+            <column>
38
+                <button name="New Cupping Session" id="createcupping" type="push" />
39
+            </column>
40
+        </row>
41
+        <row>
42
+            <column>
43
+                <button name="Join Cupping Session" id="joincupping" type="push" />
44
+            </column>
45
+        </row>
46
+        <row>
47
+            <column>
48
+                <button name="Summarize Cupping Session" id="sumcupping" type="push" />
49
+            </column>
50
+        </row>
51
+        <row>
52
+            <column>
53
+                <button name="View Target Roast Profiles" id="profilehistory" type="push" />
54
+            </column>
55
+        </row>
56
+        <row>
57
+            <column>
58
+                <button name="Import Target Roast Profiles" id="target" type="push" />
59
+            </column>
60
+        </row>
61
+        <row>
62
+            <column>
63
+                <button name="Enter Green Coffee Sales" id="greensales" type="push" />
64
+            </column>
65
+        </row>
66
+    </layout>
77
     <menu name="Reports" type="reports" src="Reports" />
67
     <menu name="Reports" type="reports" src="Reports" />
78
-	<menu name="Database">
79
-		<item id="resetconnection">Forget Connection Details</item>
80
-	</menu>
68
+    <menu name="Database">
69
+        <item id="resetconnection">Forget Connection Details</item>
70
+    </menu>
81
     <program>
71
     <program>
82
-		var window = this;
83
-		var navigationwindow = window;
84
-		window.loggingWindow = undefined;
85
-		var roasterlist = findChildObject(this, 'machineselector');
86
-		var model = new DeviceTreeModel;
87
-		roasterlist.setModel(model);
88
-		roasterlist.currentIndex = QSettings.value("machineSelection", 0);
89
-		roasterlist['currentIndexChanged(int)'].connect(function() {
90
-			QSettings.setValue("machineSelection", roasterlist.currentIndex);
91
-		});
92
-		var resetdbconnection = findChildObject(this, 'resetconnection');
93
-		resetdbconnection.triggered.connect(function() {
94
-			QSettings.setValue("database/exists", false);
95
-			QSettings.setValue("database/hostname", "");
96
-			QSettings.setValue("database/dbname", "");
97
-			QSettings.setValue("database/user", "");
98
-			QSettings.setValue("database/password", "");
99
-		});
100
-		var profilehistory = findChildObject(this, 'profilehistory');
101
-		profilehistory.clicked.connect(function() {
102
-			createWindow("profilehistory");
103
-		});
104
-		var greensalesbutton = findChildObject(this, 'greensales');
105
-		greensalesbutton.clicked.connect(function() {
106
-			createWindow("greensales");
107
-		});
108
-		var invoicesbutton = findChildObject(this, 'invoicelist');
109
-		invoicesbutton.clicked.connect(function() {
110
-			createWindow("invoicelist");
111
-		});
72
+        var window = this;
73
+        var navigationwindow = window;
74
+        window.loggingWindow = undefined;
75
+        var roasterlist = findChildObject(this, 'machineselector');
76
+        var model = new DeviceTreeModel;
77
+        roasterlist.setModel(model);
78
+        roasterlist.currentIndex = QSettings.value("machineSelection", 0);
79
+        roasterlist['currentIndexChanged(int)'].connect(function() {
80
+            QSettings.setValue("machineSelection", roasterlist.currentIndex);
81
+        });
82
+        var resetdbconnection = findChildObject(this, 'resetconnection');
83
+        resetdbconnection.triggered.connect(function() {
84
+            QSettings.setValue("database/exists", false);
85
+            QSettings.setValue("database/hostname", "");
86
+            QSettings.setValue("database/dbname", "");
87
+            QSettings.setValue("database/user", "");
88
+            QSettings.setValue("database/password", "");
89
+        });
90
+        var profilehistory = findChildObject(this, 'profilehistory');
91
+        profilehistory.clicked.connect(function() {
92
+                createWindow("profilehistory");
93
+        });
94
+        var greensalesbutton = findChildObject(this, 'greensales');
95
+        greensalesbutton.clicked.connect(function() {
96
+                createWindow("greensales");
97
+        });
112
         var sumcup = findChildObject(this, 'sumcupping');
98
         var sumcup = findChildObject(this, 'sumcupping');
113
         sumcup.clicked.connect(function() {
99
         sumcup.clicked.connect(function() {
114
             var sessionlist = createWindow("finsessionlist");
100
             var sessionlist = createWindow("finsessionlist");
136
             var invwin = createWindow("inventory");
122
             var invwin = createWindow("inventory");
137
             invwin.windowTitle = "Typica - Inventory";
123
             invwin.windowTitle = "Typica - Inventory";
138
         });
124
         });
139
-        var history = findChildObject(this, 'history');
140
-        history.clicked.connect(function() {
141
-            var histwindow = createWindow("history");
142
-            histwindow.windowTitle = "Typica - Batch Log";
125
+        var roastspecbutton = findChildObject(this, 'roastspec');
126
+        roastspecbutton.clicked.connect(function() {
127
+            var specwindow = createWindow("roastspec");
143
         });
128
         });
144
         var gbutton = findChildObject(this, 'green');
129
         var gbutton = findChildObject(this, 'green');
145
         gbutton.clicked.connect(function() {
130
         gbutton.clicked.connect(function() {
161
             {
146
             {
162
                 window.loggingWindow = createWindow("basicWindow");
147
                 window.loggingWindow = createWindow("basicWindow");
163
                 window.loggingWindow.windowTitle = "Typica [*]";
148
                 window.loggingWindow.windowTitle = "Typica [*]";
164
-				window.loggingWindow.navigationWindow = window;
149
+                window.loggingWindow.navigationWindow = window;
165
             }
150
             }
166
             else
151
             else
167
             {
152
             {
168
-				print(window.loggingWindow);
169
                 window.loggingWindow.raise();
153
                 window.loggingWindow.raise();
170
                 window.loggingWindow.activateWindow();
154
                 window.loggingWindow.activateWindow();
171
             }
155
             }
172
         });
156
         });
173
-		var configurebutton = findChildObject(this, 'configure');
174
-		configurebutton.clicked.connect(function() {
175
-			var confwindow = new SettingsWindow;
176
-			confwindow.show();
177
-		});
157
+        var configurebutton = findChildObject(this, 'configure');
158
+        configurebutton.clicked.connect(function() {
159
+            var confwindow = new SettingsWindow;
160
+            confwindow.show();
161
+        });
178
         <![CDATA[
162
         <![CDATA[
179
-		var DBCreateBase = function() {
180
-			var query = new QSqlQuery();
181
-            query.exec("CREATE TABLE IF NOT EXISTS certifications (item bigint NOT NULL, certification text NOT NULL)");
182
-            query.exec("CREATE TABLE IF NOT EXISTS cupping_samples (session bigint NOT NULL, sample text NOT NULL, position bigint NOT NULL, type text NOT NULL, \"time\" timestamp without time zone, machine bigint, point text, item bigint)");
183
-            query.exec("CREATE TABLE cupping_sessions (id bigserial NOT NULL, event text, name text NOT NULL, \"time\" timestamp without time zone NOT NULL, blind boolean NOT NULL, open boolean NOT NULL, note text)");
184
-            query.exec("CREATE TABLE IF NOT EXISTS cuppingforms (session bigint NOT NULL, sample text NOT NULL, position bigint NOT NULL, grader text, finalscore numeric, notes text, serialization text)");
185
-            query.exec("CREATE TABLE IF NOT EXISTS cuppingform_t1 (aroma numeric, flavor numeric, aftertaste numeric, acidity numeric, body numeric, uniformity numeric, balance numeric, cleancup numeric, sweetness numeric, overall numeric, total numeric) INHERITS (cuppingforms)");
186
-            query.exec("CREATE TABLE IF NOT EXISTS invoices (id bigserial PRIMARY KEY NOT NULL, invoice text, vendor text NOT NULL, \"time\" timestamp without time zone NOT NULL)");
187
-            query.exec("CREATE TABLE IF NOT EXISTS invoice_items (invoice_id bigint NOT NULL, record_type text NOT NULL, item_id bigint, description text NOT NULL, cost numeric NOT NULL)");
188
-			query.exec("CREATE TABLE IF NOT EXISTS transactions (\"time\" timestamp without time zone NOT NULL, item bigint NOT NULL)");
189
-			query.exec("CREATE TABLE IF NOT EXISTS inventory (quantity numeric NOT NULL) INHERITS (transactions)");
190
-			query.exec("CREATE TABLE IF NOT EXISTS loss (quantity numeric NOT NULL, reason text) INHERITS (transactions)");
191
-			query.exec("CREATE TABLE IF NOT EXISTS make (quantity numeric NOT NULL) INHERITS (transactions)");
192
-			query.exec("CREATE TABLE IF NOT EXISTS purchase (quantity numeric NOT NULL, cost numeric NOT NULL, vendor text NOT NULL) INHERITS (transactions)");
193
-			query.exec("CREATE TABLE IF NOT EXISTS sale (quantity numeric NOT NULL, customer text) INHERITS (transactions)");
194
-			query.exec("CREATE TABLE IF NOT EXISTS use (quantity numeric NOT NULL) INHERITS (transactions)");
195
-			query.exec("CREATE VIEW all_transactions AS ((((SELECT purchase.\"time\", purchase.item, purchase.quantity, purchase.cost, purchase.vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'PURCHASE' AS type FROM purchase UNION SELECT use.\"time\", use.item, use.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'USE' AS type FROM use) UNION SELECT inventory.\"time\", inventory.item, inventory.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'INVENTORY' AS type FROM inventory) UNION SELECT loss.\"time\", loss.item, loss.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, loss.reason, NULL::unknown AS customer, 'LOSS' AS type FROM loss) UNION SELECT make.\"time\", make.item, make.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'MAKE' AS type FROM make) UNION SELECT sale.\"time\", sale.item, sale.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, sale.customer, 'SALE' AS type FROM sale");
196
-			query.exec("CREATE FUNCTION time_range(bigint) RETURNS integer AS $$ BEGIN IF (SELECT quantity FROM items WHERE id = $1) > 0 THEN RETURN (SELECT current_date - min(time)::date + 1 FROM use WHERE item = $1); ELSE RETURN (SELECT max(time)::date - min(time)::date + 1 FROM use WHERE item = $1); END IF; END; $$ LANGUAGE plpgsql STRICT");
197
-			query.exec("CREATE TABLE IF NOT EXISTS items(id bigint NOT NULL, name text NOT NULL, reference text, unit text NOT NULL, quantity numeric DEFAULT 0, category text)");
198
-			query.exec("CREATE SEQUENCE items_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1");
199
-			query.exec("CREATE TABLE IF NOT EXISTS coffees(origin text NOT NULL, region text, producer text, grade text, milling text, drying text) INHERITS (items)");
200
-			query.exec("CREATE VIEW coffee_history AS SELECT coffees.id, coffees.name, coffees.origin, coffees.quantity AS stock, (SELECT sum(use.quantity) AS sum FROM use WHERE (use.item = coffees.id)) AS used, time_range(coffees.id) AS \"interval\", ((SELECT (sum(use.quantity) / (time_range(use.item))::numeric) FROM use WHERE (use.item = coffees.id) GROUP BY use.item))::numeric(10,2) AS rate, (SELECT (('now'::text)::date + ((coffees.quantity / (SELECT (sum(use.quantity) / (time_range(use.item))::numeric) FROM use WHERE (use.item = coffees.id) GROUP BY use.item)))::integer)) AS \"out\" FROM coffees WHERE (coffees.id IN (SELECT use.item FROM use)) ORDER BY coffees.origin");
201
-			query.exec("CREATE TABLE IF NOT EXISTS current_items (item bigint NOT NULL)");
202
-			query.exec("CREATE TABLE IF NOT EXISTS decaf_coffees (decaf_method text NOT NULL) INHERITS (coffees)");
203
-			query.exec("CREATE TABLE IF NOT EXISTS files (id bigint NOT NULL, name text NOT NULL, type text NOT NULL, note text, file bytea NOT NULL)");
204
-			query.exec("CREATE TABLE IF NOT EXISTS item_files(\"time\" timestamp without time zone NOT NULL, item bigint NOT NULL, files bigint[] NOT NULL)");
205
-			query.exec("CREATE TYPE item_transaction_with_balance AS (\"time\" timestamp without time zone, item bigint, quantity numeric, cost numeric, vendor text, reason text, customer text, type text, balance numeric)");
206
-			query.exec("CREATE TABLE IF NOT EXISTS lb_bag_conversion (item bigint NOT NULL, conversion numeric NOT NULL)");
207
-			query.exec("CREATE TABLE IF NOT EXISTS machine (id bigint NOT NULL, name text NOT NULL)");
208
-			query.exec("CREATE VIEW regular_coffees AS SELECT coffees.id, coffees.name, coffees.reference, coffees.unit, coffees.quantity, coffees.category, coffees.origin, coffees.region, coffees.producer, coffees.grade, coffees.milling, coffees.drying FROM coffees WHERE (NOT (coffees.id IN (SELECT decaf_coffees.id FROM decaf_coffees)))");
209
-			query.exec("CREATE TABLE IF NOT EXISTS roasting_log (\"time\" timestamp without time zone NOT NULL, unroasted_id bigint[], unroasted_quantity numeric[], unroasted_total_quantity numeric, roasted_id bigint, roasted_quantity numeric, transaction_type text NOT NULL, annotation text, machine bigint NOT NULL, duration interval, approval boolean, humidity numeric, barometric numeric, indoor_air numeric, outdoor_air numeric, files bigint[])");
210
-			query.exec("CREATE VIEW short_log AS SELECT roasting_log.\"time\", (SELECT items.name FROM items WHERE (items.id = roasting_log.roasted_id)) AS name, roasting_log.unroasted_total_quantity, roasting_log.roasted_quantity, ((((roasting_log.unroasted_total_quantity - roasting_log.roasted_quantity) / roasting_log.unroasted_total_quantity) * (100)::numeric))::numeric(12,2) AS weight_loss, roasting_log.duration FROM roasting_log ORDER BY roasting_log.\"time\"");
211
-			query.exec("CREATE FUNCTION add_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity + NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
212
-			query.exec("CREATE FUNCTION bags_in_stock(bigint) RETURNS numeric AS $_$SELECT quantity / (SELECT conversion FROM lb_bag_conversion WHERE item = id) FROM items WHERE id = $1;$_$ LANGUAGE sql IMMUTABLE STRICT");
213
-			query.exec("CREATE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN WHILE i <= u LOOP INSERT INTO use VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; RETURN NEW; END; $$ LANGUAGE plpgsql");
214
-			query.exec("CREATE FUNCTION replace_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
215
-			query.exec("CREATE FUNCTION subtract_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity - NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
216
-			query.exec("CREATE SEQUENCE files_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1");
217
-			query.exec("ALTER TABLE files ALTER COLUMN id SET DEFAULT nextval('files_id_seq'::regclass)");
218
-			query.exec("ALTER TABLE items ALTER COLUMN id SET DEFAULT nextval('items_id_seq'::regclass)");
219
-			query.exec("ALTER TABLE ONLY files ADD CONSTRAINT file_pkey PRIMARY KEY (id)");
220
-			query.exec("ALTER TABLE ONLY items ADD CONSTRAINT items_pkey PRIMARY KEY (id)");
221
-			query.exec("ALTER TABLE ONLY lb_bag_conversion ADD CONSTRAINT lb_bag_conversion_item_key UNIQUE (item)");
222
-			query.exec("ALTER TABLE ONLY roasting_log ADD CONSTRAINT roasting_log_pkey PRIMARY KEY (\"time\", machine)");
223
-			query.exec("CREATE INDEX itemcategories ON items USING btree (category)");
224
-			query.exec("CREATE INDEX itemnames ON items USING btree (name)");
225
-			query.exec("CREATE INDEX roasting_log_index ON roasting_log USING btree (\"time\")");
226
-			query.exec("CREATE INDEX transactionitems ON transactions USING btree (item)");
227
-			query.exec("CREATE INDEX transactiontimes ON transactions USING btree (\"time\")");
228
-			query.exec("CREATE TRIGGER add_inventory_trigger AFTER INSERT ON purchase FOR EACH ROW EXECUTE PROCEDURE add_inventory()");
229
-			query.exec("CREATE TRIGGER add_inventory_trigger AFTER INSERT ON make FOR EACH ROW EXECUTE PROCEDURE add_inventory()");
230
-			query.exec("CREATE TRIGGER log_use_trigger AFTER INSERT ON roasting_log FOR EACH ROW EXECUTE PROCEDURE log_use()");
231
-			query.exec("CREATE TRIGGER replace_inventory_trigger AFTER INSERT ON inventory FOR EACH ROW EXECUTE PROCEDURE replace_inventory()");
232
-			query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON loss FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
233
-			query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON sale FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
234
-			query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON use FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
235
-			query.exec("ALTER TABLE ONLY item_files ADD CONSTRAINT item_files_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
236
-			query.exec("ALTER TABLE ONLY transactions ADD CONSTRAINT transactions_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
237
-			query.exec("INSERT INTO TypicaFeatures (feature, enabled, version) VALUES('base-features', TRUE, 1)");
238
-			query = query.invalidate();
239
-		};
163
+            var DBCreateBase = function() {
164
+                var query = new QSqlQuery();
165
+                query.exec("CREATE TABLE IF NOT EXISTS certifications (item bigint NOT NULL, certification text NOT NULL)");
166
+                query.exec("CREATE TABLE IF NOT EXISTS cupping_samples (session bigint NOT NULL, sample text NOT NULL, position bigint NOT NULL, type text NOT NULL, \"time\" timestamp without time zone, machine bigint, point text, item bigint)");
167
+                query.exec("CREATE TABLE cupping_sessions (id bigserial NOT NULL, event text, name text NOT NULL, \"time\" timestamp without time zone NOT NULL, blind boolean NOT NULL, open boolean NOT NULL, note text)");
168
+                query.exec("CREATE TABLE IF NOT EXISTS cuppingforms (session bigint NOT NULL, sample text NOT NULL, position bigint NOT NULL, grader text, finalscore numeric, notes text, serialization text)");
169
+                query.exec("CREATE TABLE IF NOT EXISTS cuppingform_t1 (aroma numeric, flavor numeric, aftertaste numeric, acidity numeric, body numeric, uniformity numeric, balance numeric, cleancup numeric, sweetness numeric, overall numeric, total numeric) INHERITS (cuppingforms)");
170
+                query.exec("CREATE TABLE IF NOT EXISTS invoices (id bigserial PRIMARY KEY NOT NULL, invoice text, vendor text NOT NULL, \"time\" timestamp without time zone NOT NULL)");
171
+                query.exec("CREATE TABLE IF NOT EXISTS invoice_items (invoice_id bigint NOT NULL, record_type text NOT NULL, item_id bigint, description text NOT NULL, cost numeric NOT NULL)");
172
+                query.exec("CREATE TABLE IF NOT EXISTS transactions (\"time\" timestamp without time zone NOT NULL, item bigint NOT NULL)");
173
+                query.exec("CREATE TABLE IF NOT EXISTS inventory (quantity numeric NOT NULL) INHERITS (transactions)");
174
+                query.exec("CREATE TABLE IF NOT EXISTS loss (quantity numeric NOT NULL, reason text) INHERITS (transactions)");
175
+                query.exec("CREATE TABLE IF NOT EXISTS make (quantity numeric NOT NULL) INHERITS (transactions)");
176
+                query.exec("CREATE TABLE IF NOT EXISTS purchase (quantity numeric NOT NULL, cost numeric NOT NULL, vendor text NOT NULL) INHERITS (transactions)");
177
+                query.exec("CREATE TABLE IF NOT EXISTS sale (quantity numeric NOT NULL, customer text) INHERITS (transactions)");
178
+                query.exec("CREATE TABLE IF NOT EXISTS use (quantity numeric NOT NULL) INHERITS (transactions)");
179
+                query.exec("CREATE VIEW all_transactions AS ((((SELECT purchase.\"time\", purchase.item, purchase.quantity, purchase.cost, purchase.vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'PURCHASE' AS type FROM purchase UNION SELECT use.\"time\", use.item, use.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'USE' AS type FROM use) UNION SELECT inventory.\"time\", inventory.item, inventory.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'INVENTORY' AS type FROM inventory) UNION SELECT loss.\"time\", loss.item, loss.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, loss.reason, NULL::unknown AS customer, 'LOSS' AS type FROM loss) UNION SELECT make.\"time\", make.item, make.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, NULL::unknown AS customer, 'MAKE' AS type FROM make) UNION SELECT sale.\"time\", sale.item, sale.quantity, NULL::unknown AS cost, NULL::unknown AS vendor, NULL::unknown AS reason, sale.customer, 'SALE' AS type FROM sale");
180
+                query.exec("CREATE FUNCTION time_range(bigint) RETURNS integer AS $$ BEGIN IF (SELECT quantity FROM items WHERE id = $1) > 0 THEN RETURN (SELECT current_date - min(time)::date + 1 FROM use WHERE item = $1); ELSE RETURN (SELECT max(time)::date - min(time)::date + 1 FROM use WHERE item = $1); END IF; END; $$ LANGUAGE plpgsql STRICT");
181
+                query.exec("CREATE TABLE IF NOT EXISTS items(id bigint NOT NULL, name text NOT NULL, reference text, unit text NOT NULL, quantity numeric DEFAULT 0, category text)");
182
+                query.exec("CREATE SEQUENCE items_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1");
183
+                query.exec("CREATE TABLE IF NOT EXISTS coffees(origin text NOT NULL, region text, producer text, grade text, milling text, drying text) INHERITS (items)");
184
+                query.exec("CREATE VIEW coffee_history AS SELECT coffees.id, coffees.name, coffees.origin, coffees.quantity AS stock, (SELECT sum(use.quantity) AS sum FROM use WHERE (use.item = coffees.id)) AS used, time_range(coffees.id) AS \"interval\", ((SELECT (sum(use.quantity) / (time_range(use.item))::numeric) FROM use WHERE (use.item = coffees.id) GROUP BY use.item))::numeric(10,2) AS rate, (SELECT (('now'::text)::date + ((coffees.quantity / (SELECT (sum(use.quantity) / (time_range(use.item))::numeric) FROM use WHERE (use.item = coffees.id) GROUP BY use.item)))::integer)) AS \"out\" FROM coffees WHERE (coffees.id IN (SELECT use.item FROM use)) ORDER BY coffees.origin");
185
+                query.exec("CREATE TABLE IF NOT EXISTS current_items (item bigint NOT NULL)");
186
+                query.exec("CREATE TABLE IF NOT EXISTS decaf_coffees (decaf_method text NOT NULL) INHERITS (coffees)");
187
+                query.exec("CREATE TABLE IF NOT EXISTS files (id bigint NOT NULL, name text NOT NULL, type text NOT NULL, note text, file bytea NOT NULL)");
188
+                query.exec("CREATE TABLE IF NOT EXISTS item_files(\"time\" timestamp without time zone NOT NULL, item bigint NOT NULL, files bigint[] NOT NULL)");
189
+                query.exec("CREATE TYPE item_transaction_with_balance AS (\"time\" timestamp without time zone, item bigint, quantity numeric, cost numeric, vendor text, reason text, customer text, type text, balance numeric)");
190
+                query.exec("CREATE TABLE IF NOT EXISTS lb_bag_conversion (item bigint NOT NULL, conversion numeric NOT NULL)");
191
+                query.exec("CREATE TABLE IF NOT EXISTS machine (id bigint NOT NULL, name text NOT NULL)");
192
+                query.exec("CREATE VIEW regular_coffees AS SELECT coffees.id, coffees.name, coffees.reference, coffees.unit, coffees.quantity, coffees.category, coffees.origin, coffees.region, coffees.producer, coffees.grade, coffees.milling, coffees.drying FROM coffees WHERE (NOT (coffees.id IN (SELECT decaf_coffees.id FROM decaf_coffees)))");
193
+                query.exec("CREATE TABLE IF NOT EXISTS roasting_log (\"time\" timestamp without time zone NOT NULL, unroasted_id bigint[], unroasted_quantity numeric[], unroasted_total_quantity numeric, roasted_id bigint, roasted_quantity numeric, transaction_type text NOT NULL, annotation text, machine bigint NOT NULL, duration interval, approval boolean, humidity numeric, barometric numeric, indoor_air numeric, outdoor_air numeric, files bigint[])");
194
+                query.exec("CREATE VIEW short_log AS SELECT roasting_log.\"time\", (SELECT items.name FROM items WHERE (items.id = roasting_log.roasted_id)) AS name, roasting_log.unroasted_total_quantity, roasting_log.roasted_quantity, ((((roasting_log.unroasted_total_quantity - roasting_log.roasted_quantity) / roasting_log.unroasted_total_quantity) * (100)::numeric))::numeric(12,2) AS weight_loss, roasting_log.duration FROM roasting_log ORDER BY roasting_log.\"time\"");
195
+                query.exec("CREATE FUNCTION add_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity + NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
196
+                query.exec("CREATE FUNCTION bags_in_stock(bigint) RETURNS numeric AS $_$SELECT quantity / (SELECT conversion FROM lb_bag_conversion WHERE item = id) FROM items WHERE id = $1;$_$ LANGUAGE sql IMMUTABLE STRICT");
197
+                query.exec("CREATE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN WHILE i <= u LOOP INSERT INTO use VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; RETURN NEW; END; $$ LANGUAGE plpgsql");
198
+                query.exec("CREATE FUNCTION replace_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
199
+                query.exec("CREATE FUNCTION subtract_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity - NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
200
+                query.exec("CREATE SEQUENCE files_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1");
201
+                query.exec("ALTER TABLE files ALTER COLUMN id SET DEFAULT nextval('files_id_seq'::regclass)");
202
+                query.exec("ALTER TABLE items ALTER COLUMN id SET DEFAULT nextval('items_id_seq'::regclass)");
203
+                query.exec("ALTER TABLE ONLY files ADD CONSTRAINT file_pkey PRIMARY KEY (id)");
204
+                query.exec("ALTER TABLE ONLY items ADD CONSTRAINT items_pkey PRIMARY KEY (id)");
205
+                query.exec("ALTER TABLE ONLY lb_bag_conversion ADD CONSTRAINT lb_bag_conversion_item_key UNIQUE (item)");
206
+                query.exec("ALTER TABLE ONLY roasting_log ADD CONSTRAINT roasting_log_pkey PRIMARY KEY (\"time\", machine)");
207
+                query.exec("CREATE INDEX itemcategories ON items USING btree (category)");
208
+                query.exec("CREATE INDEX itemnames ON items USING btree (name)");
209
+                query.exec("CREATE INDEX roasting_log_index ON roasting_log USING btree (\"time\")");
210
+                query.exec("CREATE INDEX transactionitems ON transactions USING btree (item)");
211
+                query.exec("CREATE INDEX transactiontimes ON transactions USING btree (\"time\")");
212
+                query.exec("CREATE TRIGGER add_inventory_trigger AFTER INSERT ON purchase FOR EACH ROW EXECUTE PROCEDURE add_inventory()");
213
+                query.exec("CREATE TRIGGER add_inventory_trigger AFTER INSERT ON make FOR EACH ROW EXECUTE PROCEDURE add_inventory()");
214
+                query.exec("CREATE TRIGGER log_use_trigger AFTER INSERT ON roasting_log FOR EACH ROW EXECUTE PROCEDURE log_use()");
215
+                query.exec("CREATE TRIGGER replace_inventory_trigger AFTER INSERT ON inventory FOR EACH ROW EXECUTE PROCEDURE replace_inventory()");
216
+                query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON loss FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
217
+                query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON sale FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
218
+                query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON use FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
219
+                query.exec("ALTER TABLE ONLY item_files ADD CONSTRAINT item_files_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
220
+                query.exec("ALTER TABLE ONLY transactions ADD CONSTRAINT transactions_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
221
+                query.exec("INSERT INTO TypicaFeatures (feature, enabled, version) VALUES('base-features', TRUE, 1)");
222
+                query = query.invalidate();
223
+            };
240
 		
224
 		
241
 		/* Some changes to the database are required for sample roasting features in
225
 		/* Some changes to the database are required for sample roasting features in
242
 		   Typica 1.6 and later. */
226
 		   Typica 1.6 and later. */
293
 			query.exec("CREATE OR REPLACE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN IF NEW.transaction_type = 'ROAST' THEN WHILE i <= u LOOP INSERT INTO use (time, item, quantity) VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
277
 			query.exec("CREATE OR REPLACE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN IF NEW.transaction_type = 'ROAST' THEN WHILE i <= u LOOP INSERT INTO use (time, item, quantity) VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
294
 			query.exec("UPDATE TypicaFeatures SET version = 4 WHERE feature = 'base-features'");
278
 			query.exec("UPDATE TypicaFeatures SET version = 4 WHERE feature = 'base-features'");
295
 		};
279
 		};
296
-		
280
+                var DBUpdateReminders = function() {
281
+                    var query = new QSqlQuery;
282
+                    query.exec("CREATE TABLE IF NOT EXISTS reminders (id bigserial PRIMARY KEY NOT NULL, reminder text NOT NULL)");
283
+                    query.exec("UPDATE TypicaFeatures SET version = 5 WHERE feature = 'base-features'");
284
+                    query = query.invalidate();
285
+                };
286
+		var DBUpdateSpecification = function() {
287
+                    var query = new QSqlQuery;
288
+                    query.exec("CREATE TABLE IF NOT EXISTS roasting_specification (\"time\" timestamp without time zone NOT NULL, item bigint NOT NULL, loss numeric, tolerance numeric, notes text)");
289
+                    query.exec("UPDATE TypicaFeatures SET version = 6 WHERE feature = 'base-features'");
290
+                    query = query.invalidate();
291
+                };
292
+                
297
 		query = new QSqlQuery();
293
 		query = new QSqlQuery();
298
 		/* A table keeps track of database versioning information. This table is created
294
 		/* A table keeps track of database versioning information. This table is created
299
 		   if required. */
295
 		   if required. */
304
 		query.exec("SELECT feature, enabled, version FROM TypicaFeatures WHERE feature = 'base-features'");
300
 		query.exec("SELECT feature, enabled, version FROM TypicaFeatures WHERE feature = 'base-features'");
305
 		if(query.next())
301
 		if(query.next())
306
 		{
302
 		{
307
-			if(query.value(2) < 1)
308
-			{
309
-				DBCreateBase();
310
-			}
311
-			if(query.value(2) < 2)
312
-			{
313
-				DBUpdateMultiUser();
314
-				DBUpdateHistory();
315
-			}
316
-			if(query.value(2) < 3)
317
-			{
318
-				DBUpdateNotifications();
319
-			}
320
-			if(query.value(2) < 4)
321
-			{
322
-				DBUpdateTriggers();
323
-			}
303
+                    if(query.value(2) < 1)
304
+                    {
305
+                            DBCreateBase();
306
+                    }
307
+                    if(query.value(2) < 2)
308
+                    {
309
+                            DBUpdateMultiUser();
310
+                            DBUpdateHistory();
311
+                    }
312
+                    if(query.value(2) < 3)
313
+                    {
314
+                            DBUpdateNotifications();
315
+                    }
316
+                    if(query.value(2) < 4)
317
+                    {
318
+                            DBUpdateTriggers();
319
+                    }
320
+                    if(query.value(2) < 5)
321
+                    {
322
+                        DBUpdateReminders();
323
+                    }
324
+                    if(query.value(2) < 6)
325
+                    {
326
+                        DBUpdateSpecification();
327
+                    }
324
 		}
328
 		}
325
 		else
329
 		else
326
 		{
330
 		{

+ 484
- 275
config/Windows/newbatch.xml View File

1
 <window id="batchWindow">
1
 <window id="batchWindow">
2
     <menu name="Batch">
2
     <menu name="Batch">
3
-        <item id="new" shortcut="Ctrl+N">New Batch</item>
3
+        <item id="new" shortcut="Ctrl+N">New Batch...</item>
4
     </menu>
4
     </menu>
5
-	<layout type="horizontal">
6
-    <layout type="vertical">
7
-        <layout type="horizontal">
8
-			<label>Machine:</label>
9
-			<line id="machine" writable="false" />
10
-			<label>Unit:</label>
11
-			<sqldrop id="unit" />
12
-            <stretch />
5
+    <layout type="horizontal">
6
+        <layout type="vertical">
7
+            <layout type="horizontal">
8
+                <label>Machine:</label>
9
+                <line id="machine" writable="false" />
10
+                <label>Unit:</label>
11
+                <sqldrop id="unit" />
12
+                <stretch />
13
+            </layout>
14
+            <layout type="horizontal">
15
+                <label>Roasted Coffee:</label>
16
+                <sqldrop data="0" display="1" showdata="true" id="roasted">
17
+                    <null />
18
+                    <query>SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id IN (SELECT item FROM current_items) ORDER BY name</query>
19
+                </sqldrop>
20
+                <stretch />
21
+            </layout>
22
+            <label>Green Coffee:</label>
23
+            <sqltablearray columns="3" id="greens">
24
+                <column name="Coffee" delegate="sql" showdata="true" null="true" nulltext="Delete" nulldata="delete" data="0" display="1">SELECT id, name FROM coffees WHERE quantity &lt;&gt; 0 ORDER BY name</column>
25
+                <column name="Weight" delegate="numeric" />
26
+                <column name="Remaining" />
27
+            </sqltablearray>
28
+            <layout type="horizontal">
29
+                <label>Green Weight:</label>
30
+                <line id="green" writable="false">0.0</line>
31
+            </layout>
32
+            <layout type="horizontal">
33
+                <button name="Load Profile" type="push" id="load" />
34
+                <button name="No Profile" type="push" id="noprofile" />
35
+            </layout>
36
+            <layout type="horizontal">
37
+                <label>Time:</label>
38
+                <line id="time" writable="false" />
39
+                <label>Duration:</label>
40
+                <line id="duration" writable="false" />
41
+            </layout>
42
+            <layout type="horizontal">
43
+                <label>Roasted Weight:</label>
44
+                <line id="roast" validator="numeric">0.0</line>
45
+                <label>Weight Loss:</label>
46
+                <line id="wloss" writable="false" />
47
+                <button type="check" id="approval" name="Approved" />
48
+            </layout>
49
+            <layout type="horizontal">
50
+                <label>Annotation:</label>
51
+                <textarea id="annotation" />
52
+            </layout>
53
+            <layout type="horizontal">
54
+                <button name="Submit" id="submit" type="push" />
55
+                <button name="Save log as target profile" type="check" id="target" />
56
+            </layout>
13
         </layout>
57
         </layout>
14
-        <layout type="horizontal">
15
-            <label>Roasted Coffee:</label>
16
-            <sqldrop data="0" display="1" showdata="true" id="roasted">
17
-                <null />
18
-                <query>SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id IN (SELECT item FROM current_items) ORDER BY name</query>
19
-            </sqldrop>
58
+        <layout type="vertical">
59
+            <label>Connected Scales</label>
60
+            <layout type="vertical" id="scales" />
61
+            <label>Expected Weight Loss</label>
62
+            <line id="lossspec" writable="false" />
63
+            <label>Expected Roasted Weight</label>
64
+            <layout type="horizontal">
65
+                <label>Min:</label>
66
+                <line id="minroastweight" writable="false" />
67
+            </layout>
68
+            <layout type="horizontal">
69
+                <label>Expected:</label>
70
+                <line id="expectedroastweight" writable="false" />
71
+            </layout>
72
+            <layout type="horizontal">
73
+                <label>Max:</label>
74
+                <line id="maxroastweight" writable="false" />
75
+            </layout>
76
+            <label>Specification Details</label>
77
+            <textarea id="specnotes" />
20
             <stretch />
78
             <stretch />
21
         </layout>
79
         </layout>
22
-        <label>Green Coffee:</label>
23
-        <sqltablearray columns="2" id="greens">
24
-            <column name="Coffee" delegate="sql" showdata="true" null="true" nulltext="Delete" nulldata="delete" data="0" display="1">SELECT id, name FROM coffees WHERE quantity &lt;&gt; 0 ORDER BY name</column>
25
-            <column name="Weight" delegate="numeric" />
26
-        </sqltablearray>
27
-        <layout type="horizontal">
28
-            <label>Green Weight:</label>
29
-            <line id="green" writable="false">0.0</line>
30
-        </layout>
31
-        <layout type="horizontal">
32
-            <button name="Load Profile" type="push" id="load" />
33
-            <button name="No Profile" type="push" id="noprofile" />
34
-        </layout>
35
-        <layout type="horizontal">
36
-            <label>Time:</label>
37
-            <line id="time" writable="false" />
38
-            <label>Duration:</label>
39
-            <line id="duration" writable="false" />
40
-        </layout>
41
-        <layout type="horizontal">
42
-            <label>Roasted Weight:</label>
43
-            <line id="roast" validator="numeric" />
44
-            <label>Weight Loss:</label>
45
-            <line id="wloss" writable="false" />
46
-            <button type="check" id="approval" name="Approved" />
47
-        </layout>
48
-        <layout type="horizontal">
49
-            <label>Annotation:</label>
50
-            <textarea id="annotation" />
51
-        </layout>
52
-        <layout type="horizontal">
53
-            <button name="Submit" id="submit" type="push" />
54
-            <button name="Save log as target profile" type="check" id="target" />
55
-        </layout>
56
     </layout>
80
     </layout>
57
-	<layout type="vertical">
58
-		<label>Connected Scales</label>
59
-		<layout type="vertical" id="scales" />
60
-		<stretch />
61
-	</layout>
62
-	</layout>
63
     <program>
81
     <program>
64
         <![CDATA[
82
         <![CDATA[
65
-			var unitBox = findChildObject(this, 'unit');
66
-			unitBox.addItem("g");
67
-			unitBox.addItem("Kg");
68
-			unitBox.addItem("oz");
69
-			unitBox.addItem("lb");
70
-			unitBox.currentIndex = (QSettings.value("script/batch_unit", unitBox.findText("lb")));
71
-			var machine = findChildObject(this, "machine");
72
-			machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
73
-			var newMenu = findChildObject(this, 'new');
74
-			newMenu.triggered.connect(function() {
75
-				var bwindow = createWindow("batchWindow");
76
-				bwindow.windowTitle = "Typica - [*]New Batch";
77
-			});
78
-			var batch = this;
79
-			var table = findChildObject(this, 'greens');
80
-			var green = findChildObject(this, 'green');
81
-			var model = table.model();
83
+            var unitBox = findChildObject(this, 'unit');
84
+            unitBox.addItem("g");
85
+            unitBox.addItem("Kg");
86
+            unitBox.addItem("oz");
87
+            unitBox.addItem("lb");
88
+            unitBox.currentIndex = (QSettings.value("script/batch_unit", unitBox.findText("lb")));
89
+            unitBox['currentIndexChanged(int)'].connect(function() {
90
+                QSettings.setValue("script/batch_unit", unitBox.currentIndex);
91
+            });
92
+            var machine = findChildObject(this, "machine");
93
+            machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
94
+            var newMenu = findChildObject(this, 'new');
95
+            newMenu.triggered.connect(function() {
96
+                var bwindow = createWindow("batchWindow");
97
+                bwindow.windowTitle = "Typica - [*]New Batch";
98
+            });
99
+            var batch = this;
100
+            var table = findChildObject(this, 'greens');
101
+            var green = findChildObject(this, 'green');
102
+            var model = table.model();
82
             var lossField = findChildObject(this, 'wloss');
103
             var lossField = findChildObject(this, 'wloss');
83
             lossField.maximumWidth = 80;
104
             lossField.maximumWidth = 80;
84
             var roasted = findChildObject(this, 'roasted');
105
             var roasted = findChildObject(this, 'roasted');
85
             var roastwt = findChildObject(this, 'roast');
106
             var roastwt = findChildObject(this, 'roast');
86
             roastwt.maximumWidth = 80;
107
             roastwt.maximumWidth = 80;
87
-			var scalesLayout = findChildObject(this, 'scales');
88
-			scalesLayout.spacing = 10;
89
-			if(navigationwindow.loggingWindow.scales.length > 0) {
90
-				for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
91
-					var scale = navigationwindow.loggingWindow.scales[i];
92
-					var label = new DragLabel();
93
-					var weighButton = new QPushButton();
94
-					weighButton.text = "Weigh";
95
-					weighButton.clicked.connect(scale.weigh);
96
-					label.updateMeasurement = function(m, u) {
97
-						switch(unitBox.currentIndex) {
98
-							case 0:
99
-								this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
100
-								break;
101
-							case 1:
102
-								this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
103
-								break;
104
-							case 2:
105
-								this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
106
-								break;
107
-							case 3:
108
-								this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
109
-								break;
110
-						}
111
-					};
112
-					scalesLayout.addWidget(label);
113
-					scalesLayout.addWidget(weighButton);
114
-					scale.newMeasurement.connect(function(m, u) {
115
-						label.updateMeasurement(m, u);
116
-					});
117
-					scale.weigh();
118
-					unitBox['currentIndexChanged(int)'].connect(scale.weigh);
119
-				}
120
-			}
121
-			model.dataChanged.connect(function() {
122
-				var deleteRow = -1;
123
-				/* The combo box delegate updates user data before display data
124
-				   and this code is executed before the model update is fully
125
-				   complete. Rather than rely on this behavior continuing, we
126
-				   check that the display value has also been updated and defer
127
-				   row removal until both updates are complete.
128
-				*/
129
-				while((deleteRow = table.findData("delete", 0)) > -1) {
130
-					if(table.data(deleteRow, 0, 0) == "Delete") {
131
-						table.removeRow(table.findData("delete", 0));
132
-					} else {
133
-						break;
134
-					}
135
-				}
136
-				green.text = table.columnSum(1, 0);
137
-				table.resizeColumnToContents(0);
108
+            var scalesLayout = findChildObject(this, 'scales');
109
+            scalesLayout.spacing = 10;
110
+            if(navigationwindow.loggingWindow.scales.length > 0) {
111
+                for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
112
+                    var scale = navigationwindow.loggingWindow.scales[i];
113
+                    var label = new DragLabel();
114
+                    var weighButton = new QPushButton();
115
+                    weighButton.text = "Weigh";
116
+                    weighButton.clicked.connect(scale.weigh);
117
+                    label.updateMeasurement = function(m, u) {
118
+                        switch(unitBox.currentIndex) {
119
+                            case 0:
120
+                                this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
121
+                                break;
122
+                            case 1:
123
+                                this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
124
+                                break;
125
+                            case 2:
126
+                                this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
127
+                                break;
128
+                            case 3:
129
+                                this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
130
+                                break;
131
+                        }
132
+                    };
133
+                    scalesLayout.addWidget(label);
134
+                    scalesLayout.addWidget(weighButton);
135
+                    scale.newMeasurement.connect(function(m, u) {
136
+                        label.updateMeasurement(m, u);
137
+                    });
138
+                    scale.weigh();
139
+                    unitBox['currentIndexChanged(int)'].connect(scale.weigh);
140
+                }
141
+            }
142
+            var remainingStock = new Array();
143
+            var query = new QSqlQuery();
144
+            query.exec("SELECT id, quantity, (SELECT conversion FROM lb_bag_conversion WHERE item = id) FROM coffees WHERE quantity <> 0");
145
+            while(query.next()) {
146
+                remainingStock.push({id: query.value(0),
147
+                                     quantity: query.value(1),
148
+                                     conversion: query.value(2)});
149
+            }
150
+            query = query.invalidate();
151
+            var convertToPounds = function(w, u) {
152
+                switch(u) {
153
+                    case "g":
154
+                        return w * 0.0022;
155
+                    case "oz":
156
+                        return w * 0.0625;
157
+                    case "Kg":
158
+                        return w * 2.2;
159
+                }
160
+                return w;
161
+            };
162
+            var convertFromPounds = function(w, u) {
163
+                switch(u) {
164
+                    case "g":
165
+                        return w / 0.0022;
166
+                    case "oz":
167
+                        return w / 0.0625;
168
+                    case "Kg":
169
+                        return w / 2.2;
170
+                }
171
+                return w;
172
+            };
173
+            var specnotes = findChildObject(this, 'specnotes');
174
+            specnotes.readOnly = true;
175
+            var lossspec = findChildObject(this, 'lossspec');
176
+            var minfield = findChildObject(this, 'minroastweight');
177
+            var midfield = findChildObject(this, 'expectedroastweight');
178
+            var maxfield = findChildObject(this, 'maxroastweight');
179
+            var minloss = 0;
180
+            var maxloss = 0;
181
+            var expectloss = 0;
182
+            var updateGreenTable = function() {
183
+                var deleteRow = -1;
184
+                /* The combo box delegate updates user data before display data
185
+                and this code is executed before the model update is fully
186
+                complete. Rather than rely on this behavior continuing, we
187
+                check that the display value has also been updated and defer
188
+                row removal until both updates are complete.
189
+                */
190
+                while((deleteRow = table.findData("delete", 0)) > -1) {
191
+                    if(table.data(deleteRow, 0, 0) == "Delete") {
192
+                        table.removeRow(table.findData("delete", 0));
193
+                    } else {
194
+                        break;
195
+                    }
196
+                }
197
+                green.text = table.columnSum(1, 0);
198
+                table.resizeColumnToContents(0);
199
+                var gid = 0;
200
+                var r = 0;
201
+                while(gid >= 0)
202
+                {
203
+                    gid = Number(table.data(r, 0, 32));
204
+                    if(isNaN(gid))
205
+                    {
206
+                        gid = -1;
207
+                        break;
208
+                    }
209
+                    var bagConversion = 1;
210
+                    for(var i = 0; i < remainingStock.length; i++)
211
+                    {
212
+                        if(gid == Number(remainingStock[i].id))
213
+                        {
214
+                            var displayValue = Number(remainingStock[i].quantity);
215
+                            bagConversion = Number(remainingStock[i].conversion);
216
+                            if(!isNaN(Number(table.data(r, 1, 0))))
217
+                            {
218
+                                var change = Number(table.data(r, 1, 0));
219
+                                switch(unitBox.currentIndex)
220
+                                {
221
+                                    case 0:
222
+                                        change = convertToPounds(change, "g");
223
+                                        break;
224
+                                    case 1:
225
+                                        change = convertToPounds(change, "Kg");
226
+                                        break;
227
+                                    case 2:
228
+                                        change = convertToPounds(change, "oz");
229
+                                        break;
230
+                                }
231
+                                displayValue -= change;
232
+                            }
233
+                            var bagCount = (displayValue / bagConversion).toFixed(2);
234
+                            switch(unitBox.currentIndex)
235
+                            {
236
+                                case 0:
237
+                                    displayValue = convertFromPounds(displayValue, "g");
238
+                                    break;
239
+                                case 1:
240
+                                    displayValue = convertFromPounds(displayValue, "Kg");
241
+                                    break;
242
+                                case 2:
243
+                                    displayValue = convertFromPounds(displayValue, "oz");
244
+                                    break;
245
+                            }
246
+                            displayValue = "" + Number(displayValue).toFixed(3) + " (" + Number(bagCount).toFixed(3) + " bags)";
247
+                            if(table.data(r, 2, 0) != displayValue)
248
+                            {
249
+                                table.setData(r, 2, displayValue, 0);
250
+                                table.setData(r, 2, displayValue, 2);
251
+                                table.resizeColumnToContents(2);
252
+                            }
253
+                        }
254
+                    }
255
+                    r++;
256
+                }
138
                 if(parseFloat(green.text) > 0)
257
                 if(parseFloat(green.text) > 0)
139
                 {
258
                 {
259
+                    var expectedLossDesc = "";
260
+                    if(lossspec.text.length > 0) {
261
+                        if(minloss != expectloss) {
262
+                            minfield.text = (-(parseFloat(green.text)) * (maxloss - 1)).toFixed(2);
263
+                            maxfield.text = (-(parseFloat(green.text)) * (minloss - 1)).toFixed(2);
264
+                        }
265
+                        midfield.text = (-(parseFloat(green.text)) * (expectloss - 1)).toFixed(2);
266
+                    }
140
                     if(parseFloat(roastwt.text) > 0)
267
                     if(parseFloat(roastwt.text) > 0)
141
                     {
268
                     {
142
                         lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
269
                         lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
146
                         lossField.text = "100%";
273
                         lossField.text = "100%";
147
                     }
274
                     }
148
                 }
275
                 }
149
-			});
276
+            };
277
+            model.dataChanged.connect(updateGreenTable);
278
+            unitBox['currentIndexChanged(int)'].connect(updateGreenTable);
150
             roastwt.textChanged.connect(function() {
279
             roastwt.textChanged.connect(function() {
151
                 if(parseFloat(green.text) > 0)
280
                 if(parseFloat(green.text) > 0)
152
                 {
281
                 {
160
                     }
289
                     }
161
                 }
290
                 }
162
             });
291
             });
163
-			var convertToPounds = function(w, u) {
164
-				switch(u) {
165
-					case "g":
166
-						return w * 0.0022;
167
-					case "oz":
168
-						return w * 0.0625;
169
-					case "Kg":
170
-						return w * 2.2;
171
-				}
172
-				return w;
173
-			};
174
             var profilebutton = findChildObject(this, 'load');
292
             var profilebutton = findChildObject(this, 'load');
175
             profilebutton.setEnabled(false);
293
             profilebutton.setEnabled(false);
176
             roasted['currentIndexChanged(int)'].connect(function() {
294
             roasted['currentIndexChanged(int)'].connect(function() {
177
-				table.clear();
295
+                table.clear();
178
                 var query = new QSqlQuery();
296
                 var query = new QSqlQuery();
179
                 var q = "SELECT EXISTS(SELECT 1 FROM item_files WHERE item = ";
297
                 var q = "SELECT EXISTS(SELECT 1 FROM item_files WHERE item = ";
180
                 q = q + roasted.currentData();
298
                 q = q + roasted.currentData();
234
                         }
352
                         }
235
                     }
353
                     }
236
                 }
354
                 }
355
+                query.prepare("SELECT loss, tolerance, notes FROM roasting_specification WHERE item = :id1 AND time = (SELECT max(time) FROM roasting_specification WHERE item = :id2)");
356
+                query.bind(":id1", roasted.currentData());
357
+                query.bind(":id2", roasted.currentData());
358
+                query.exec();
359
+                var lossSpecDescription = "";
360
+                if(query.next()) {
361
+                    if(query.value(0).length > 0) {
362
+                        lossSpecDescription += (Number(query.value(0)) * 100).toFixed(2);
363
+                        minloss = Number(query.value(0));
364
+                        maxloss = Number(query.value(0));
365
+                        expectloss = Number(query.value(0));
366
+                    }
367
+                    if(query.value(1).length > 0) {
368
+                        lossSpecDescription += " +/- " + (Number(query.value(1)) * 100).toFixed(2);
369
+                        minloss -= Number(query.value(1));
370
+                        maxloss += Number(query.value(1));
371
+                    }
372
+                    if(lossSpecDescription.length > 0) {
373
+                        lossSpecDescription += "%";
374
+                    }
375
+                    lossspec.text = lossSpecDescription;
376
+                    specnotes.plainText = query.value(2);
377
+                } else {
378
+                    lossspec.text = "";
379
+                    specnotes.plainText = "";
380
+                }
381
+                roastestimate.text = "";
382
+                query = query.invalidate();
383
+            });
384
+            var validateCapacity = function() {
385
+                if(checkCapacity == "true") {
386
+                    if(convertToPounds(parseFloat(green.text), unitBox.currentText) > poundsCapacity) {
387
+                        return false;
388
+                    }
389
+                }
390
+                return true;
391
+            }
392
+            profilebutton.clicked.connect(function() {
393
+                var proceed = false;
394
+                if(validateCapacity()) {
395
+                    proceed = true;
396
+                } else {
397
+                    proceed = displayWarning(TTR("batchWindow", "Suspicious Input"),
398
+                    TTR("batchWindow", "Entered green coffee weight exceeds maximum batch size. Continue?"));
399
+                }
400
+                if(proceed) {
401
+                    doLoadProfile();
402
+                }
237
             });
403
             });
238
-			profilebutton.clicked.connect(function() {
239
-				batch.windowModified = true;
240
-				currentBatchInfo = batch;
241
-				query = new QSqlQuery();
404
+            var doLoadProfile = function() {
405
+                batch.windowModified = true;
406
+                currentBatchInfo = batch;
407
+                query = new QSqlQuery();
242
                 var q = "SELECT files FROM item_files WHERE item = :item AND time = (SELECT max(time) FROM item_files WHERE item = :again)";
408
                 var q = "SELECT files FROM item_files WHERE item = :item AND time = (SELECT max(time) FROM item_files WHERE item = :again)";
243
-				query.prepare(q);
409
+                query.prepare(q);
244
                 query.bind(":item", Number(roasted.currentData()));
410
                 query.bind(":item", Number(roasted.currentData()));
245
                 query.bind(":again", Number(roasted.currentData()));
411
                 query.bind(":again", Number(roasted.currentData()));
246
-				query.exec();
247
-				var graph;
248
-				var log;
249
-				if(query.next())
250
-				{
251
-					var files = query.value(0);
252
-					files = files.replace("{", "(");
253
-					files = files.replace("}", ")");
254
-					q = "SELECT file, name FROM files WHERE id IN ";
255
-					q = q + files;
256
-					q = q + " AND type = 'profile'";
257
-					query.exec(q);
258
-					if(query.next())
259
-					{
260
-						var targetseries = -1;
261
-						var buffer = new QBuffer(query.value(0));
262
-						var pname = query.value(1);
263
-						var input = new XMLInput(buffer, 1);
264
-						graph = findChildObject(navigationwindow.loggingWindow, 'graph');
265
-						log = findChildObject(navigationwindow.loggingWindow, 'log');
266
-						log.clear();
267
-						graph.clear();
268
-						input.newTemperatureColumn.connect(log.setHeaderData);
269
-						input.newTemperatureColumn.connect(function(col, text) {
270
-							if(text == navigationwindow.loggingWindow.targetcolumnname)
271
-							{
272
-								targetseries = col;
273
-							}
274
-						});
275
-						input.newAnnotationColumn.connect(log.setHeaderData);
276
-						input.measure.connect(graph.newMeasurement);
277
-						input.measure.connect(log.newMeasurement);
278
-						input.measure.connect(function(data, series) {
279
-							if(series == targetseries)
280
-							{
281
-								targetDetector.newMeasurement(data);
282
-							}
283
-						});
284
-						var lc;
285
-						input.lastColumn.connect(function(c) {
286
-							lc = c;
287
-							QSettings.setValue("liveColumn", c + 1);
288
-							navigationwindow.loggingWindow.postLoadColumnSetup(c)
289
-						});
290
-						input.annotation.connect(function(note, tcol, ncol) {
291
-							for(var i = tcol; i < ncol; i++) {
292
-								log.newAnnotation(note, i, ncol);
293
-							}
294
-						});
295
-					}
296
-				}
412
+                query.exec();
413
+                var graph;
414
+                var log;
415
+                if(query.next())
416
+                {
417
+                    var files = query.value(0);
418
+                    files = files.replace("{", "(");
419
+                    files = files.replace("}", ")");
420
+                    q = "SELECT file, name FROM files WHERE id IN ";
421
+                    q = q + files;
422
+                    q = q + " AND type = 'profile'";
423
+                    query.exec(q);
424
+                    if(query.next())
425
+                    {
426
+                        var targetseries = -1;
427
+                        var buffer = new QBuffer(query.value(0));
428
+                        var pname = query.value(1);
429
+                        var input = new XMLInput(buffer, 1);
430
+                        graph = findChildObject(navigationwindow.loggingWindow, 'graph');
431
+                        log = findChildObject(navigationwindow.loggingWindow, 'log');
432
+                        log.clear();
433
+                        graph.clear();
434
+                        input.newTemperatureColumn.connect(log.setHeaderData);
435
+                        input.newTemperatureColumn.connect(function(col, text) {
436
+                            if(text == navigationwindow.loggingWindow.targetcolumnname)
437
+                            {
438
+                                targetseries = col;
439
+                            }
440
+                        });
441
+                        input.newAnnotationColumn.connect(log.setHeaderData);
442
+                        input.measure.connect(graph.newMeasurement);
443
+                        input.measure.connect(log.newMeasurement);
444
+                        input.measure.connect(function(data, series) {
445
+                            if(series == targetseries)
446
+                            {
447
+                                targetDetector.newMeasurement(data);
448
+                            }
449
+                        });
450
+                        var lc;
451
+                        input.lastColumn.connect(function(c) {
452
+                            lc = c;
453
+                            QSettings.setValue("liveColumn", c + 1);
454
+                            navigationwindow.loggingWindow.postLoadColumnSetup(c)
455
+                        });
456
+                        input.annotation.connect(function(note, tcol, ncol) {
457
+                            for(var i = tcol; i < ncol; i++) {
458
+                                log.newAnnotation(note, i, ncol);
459
+                            }
460
+                        });
461
+                    }
462
+                }
297
                 query = query.invalidate();
463
                 query = query.invalidate();
298
-				navigationwindow.loggingWindow.windowTitle = "Typica - [*]" + pname;
299
-				navigationwindow.loggingWindow.raise();
300
-				navigationwindow.loggingWindow.activateWindow();
301
-				graph.updatesEnabled = false;
302
-				log.updatesEnabled = false;
303
-				input.input();
304
-				log.updatesEnabled = true;
305
-				graph.updatesEnabled = true;
306
-				log.newAnnotation("End", 1, lc);
307
-			});
308
-			var noprofilebutton = findChildObject(this, 'noprofile');
309
-			noprofilebutton.clicked.connect(function() {
310
-				batch.windowModified = true;
311
-				currentBatchInfo = batch;
312
-				navigationwindow.loggingWindow.raise();
313
-				navigationwindow.loggingWindow.activateWindow();
314
-			});
315
-			var submitbutton = findChildObject(this, 'submit');
316
-			var timefield = findChildObject(this, 'time');
317
-			var notes = findChildObject(this, 'annotation');
318
-			var duration = findChildObject(this, 'duration');
319
-			var approval = findChildObject(this, 'approval');
320
-			approval.checked = true;
321
-			var target = findChildObject(this, 'target');
322
-			submitbutton.clicked.connect(function() {
323
-				checkQuery = new QSqlQuery();
324
-				checkQuery.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
325
-				if(!checkQuery.next())
326
-				{
327
-					checkQuery.prepare("INSERT INTO machine (id, name) VALUES(:id, :name)");
328
-					checkQuery.bind(":id", selectedRoasterID);
329
-					checkQuery.bind(":name", selectedRoasterName);
330
-					checkQuery.exec();
331
-				}
332
-				checkQuery = checkQuery.invalidate();
333
-				var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
334
-				query = new QSqlQuery();
335
-				query.prepare(q);
336
-				query.bind(":name", timefield.text + " " + roasted.currentText);
337
-				query.bindFileData(":data", batch.tempData);
338
-				query.exec();
339
-				query.next();
340
-				var fileno = query.value(0);
341
-				var file = new QFile(batch.tempData);
342
-				file.remove();
343
-				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, ";
344
-				q2 = q2 + table.columnArray(0, 32);
345
-				q2 = q2 + ", ";
346
-				for(var i = 0; table.data(i, 1, 0).value != ""; i++)
347
-				{
348
-					table.setData(i, 1, convertToPounds(parseFloat(table.data(i, 1, 0)), unitBox.currentText) ,32)
349
-				}
350
-				q2 = q2 + table.columnArray(1, 32);
351
-				q2 = q2 + ", ";
352
-				q2 = q2 + convertToPounds(parseFloat(green.text), unitBox.currentText);
353
-				q2 = q2 + ", ";
354
-				q2 = q2 + roasted.currentData();
355
-				q2 = q2 + ", ";
356
-				q2 = q2 + convertToPounds(parseFloat(roastwt.text), unitBox.currentText);
357
-				q2 = q2 + ", 'ROAST', :annotation, ";
358
-				q2 = q2 + selectedRoasterID;
359
-				q2 = q2 + ", :duration, :approval, NULL, NULL, NULL, NULL, '{";
360
-				q2 = q2 + fileno;
361
-				q2 = q2 + "}')";
362
-				query2 = new QSqlQuery();
363
-				query2.prepare(q2);
364
-				query2.bind(":time", timefield.text);
365
-				query2.bind(":annotation", notes.plainText);
366
-				query2.bind(":duration", duration.text);
367
-				query2.bind(":approval", approval.checked);
368
-				query2.exec();
464
+                navigationwindow.loggingWindow.windowTitle = "Typica - [*]" + pname;
465
+                navigationwindow.loggingWindow.raise();
466
+                navigationwindow.loggingWindow.activateWindow();
467
+                graph.updatesEnabled = false;
468
+                log.updatesEnabled = false;
469
+                input.input();
470
+                log.updatesEnabled = true;
471
+                graph.updatesEnabled = true;
472
+                log.newAnnotation(TTR("batchWindow", "End"), 1, lc);
473
+            }
474
+            var noprofilebutton = findChildObject(this, 'noprofile');
475
+            noprofilebutton.clicked.connect(function() {
476
+                var proceed = false;
477
+                if(validateCapacity()) {
478
+                    proceed = true;
479
+                } else {
480
+                    proceed = displayWarning(TTR("batchWindow", "Suspicious Input"),
481
+                    TTR("batchWindow", "Entered green coffee weight exceeds maximum batch size. Continue?"));
482
+                }
483
+                if(proceed) {
484
+                    doNoProfile();
485
+                }
486
+            });
487
+            var doNoProfile = function() {
488
+                batch.windowModified = true;
489
+                currentBatchInfo = batch;
490
+                navigationwindow.loggingWindow.raise();
491
+                navigationwindow.loggingWindow.activateWindow();
492
+            }
493
+            var submitbutton = findChildObject(this, 'submit');
494
+            var timefield = findChildObject(this, 'time');
495
+            var notes = findChildObject(this, 'annotation');
496
+            var duration = findChildObject(this, 'duration');
497
+            var approval = findChildObject(this, 'approval');
498
+            approval.checked = true;
499
+            var target = findChildObject(this, 'target');
500
+            var checkSubmitEnable = function () {
501
+                if(roasted.currentIndex > 0) {
502
+                    if(timefield.text.length > 0) {
503
+                        if(duration.text.length > 0) {
504
+                            if(batch.tempData.length > 0) {
505
+                                if(green.text.length > 0) {
506
+                                    return true;
507
+                                }
508
+                            }
509
+                        }
510
+                    }
511
+                }
512
+                return false;
513
+            }
514
+            submitbutton.clicked.connect(function() {
515
+                var proceed = false;
516
+                if(validateCapacity()) {
517
+                    proceed = true;
518
+                } else {
519
+                    proceed = displayWarning(TTR("batchWindow", "Suspicious Input"),
520
+                    TTR("batchWindow", "Entered green coffee weight exceeds maximum batch size. Continue?"));
521
+                }
522
+                if(proceed) {
523
+                    if(checkSubmitEnable()) {
524
+                        doSubmit();
525
+                    } else {
526
+                        displayError(TTR("batchWindow", "Incomplete Input"),
527
+                        TTR("batchWindow", "Some required information is not available. Please check inputs and try again."));
528
+                    }
529
+                }
530
+            });
531
+            var doSubmit = function() {
532
+                checkQuery = new QSqlQuery();
533
+                checkQuery.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
534
+                if(!checkQuery.next())
535
+                {
536
+                    checkQuery.prepare("INSERT INTO machine (id, name) VALUES(:id, :name)");
537
+                    checkQuery.bind(":id", selectedRoasterID);
538
+                    checkQuery.bind(":name", selectedRoasterName);
539
+                    checkQuery.exec();
540
+                }
541
+                checkQuery = checkQuery.invalidate();
542
+                var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
543
+                query = new QSqlQuery();
544
+                query.prepare(q);
545
+                query.bind(":name", timefield.text + " " + roasted.currentText);
546
+                query.bindFileData(":data", batch.tempData);
547
+                query.exec();
548
+                query.next();
549
+                var fileno = query.value(0);
550
+                var file = new QFile(batch.tempData);
551
+                file.remove();
552
+                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, ";
553
+                q2 = q2 + table.columnArray(0, 32);
554
+                q2 = q2 + ", ";
555
+                for(var i = 0; table.data(i, 1, 0).value != ""; i++)
556
+                {
557
+                    table.setData(i, 1, convertToPounds(parseFloat(table.data(i, 1, 0)), unitBox.currentText) ,32)
558
+                }
559
+                q2 = q2 + table.columnArray(1, 32);
560
+                q2 = q2 + ", ";
561
+                q2 = q2 + convertToPounds(parseFloat(green.text), unitBox.currentText);
562
+                q2 = q2 + ", ";
563
+                q2 = q2 + roasted.currentData();
564
+                q2 = q2 + ", ";
565
+                q2 = q2 + convertToPounds(parseFloat(roastwt.text), unitBox.currentText);
566
+                q2 = q2 + ", 'ROAST', :annotation, ";
567
+                q2 = q2 + selectedRoasterID;
568
+                q2 = q2 + ", :duration, :approval, NULL, NULL, NULL, NULL, '{";
569
+                q2 = q2 + fileno;
570
+                q2 = q2 + "}')";
571
+                query2 = new QSqlQuery();
572
+                query2.prepare(q2);
573
+                query2.bind(":time", timefield.text);
574
+                query2.bind(":annotation", notes.plainText);
575
+                query2.bind(":duration", duration.text);
576
+                query2.bind(":approval", approval.checked);
577
+                query2.exec();
369
                 query2 = query2.invalidate();
578
                 query2 = query2.invalidate();
370
-				if(target.checked) {
371
-					var q3 = "INSERT INTO item_files (time, item, files) VALUES(:time, :item, '{";
372
-					q3 = q3 + fileno;
373
-					q3 = q3 + "}')";
374
-					query.prepare(q3);
375
-					query.bind(":time", timefield.text);
376
-					query.bind(":item", roasted.currentData());
377
-					query.exec();
378
-				}
579
+                if(target.checked) {
580
+                    var q3 = "INSERT INTO item_files (time, item, files) VALUES(:time, :item, '{";
581
+                    q3 = q3 + fileno;
582
+                    q3 = q3 + "}')";
583
+                    query.prepare(q3);
584
+                    query.bind(":time", timefield.text);
585
+                    query.bind(":item", roasted.currentData());
586
+                    query.exec();
587
+                }
379
                 query = query.invalidate();
588
                 query = query.invalidate();
380
-				batch.windowModified = false;
381
-				batch.close();
382
-			});
589
+                batch.windowModified = false;
590
+                batch.close();
591
+            }
383
         ]]>
592
         ]]>
384
     </program>
593
     </program>
385
 </window>
594
 </window>

+ 42
- 9
config/Windows/newsamplebatch.xml View File

98
 					var scale = navigationwindow.loggingWindow.scales[i];
98
 					var scale = navigationwindow.loggingWindow.scales[i];
99
 					var label = new DragLabel();
99
 					var label = new DragLabel();
100
 					var weighButton = new QPushButton();
100
 					var weighButton = new QPushButton();
101
-					weighButton.text = "Weigh";
101
+					weighButton.text = TTR("sampleRoastingBatch", "Weigh");
102
 					weighButton.clicked.connect(scale.weigh);
102
 					weighButton.clicked.connect(scale.weigh);
103
 					label.updateMeasurement = function(m, u) {
103
 					label.updateMeasurement = function(m, u) {
104
 						switch(GunitBox.currentIndex) {
104
 						switch(GunitBox.currentIndex) {
127
 			}
127
 			}
128
 			var submit = findChildObject(this, 'submit');
128
 			var submit = findChildObject(this, 'submit');
129
 			submit.setEnabled(false);
129
 			submit.setEnabled(false);
130
-			this.windowTitle = "Typica - New Sample Roasting Batch";
130
+			this.windowTitle = TTR("sampleRoastingBatch", "Typica - New Sample Roasting Batch");
131
 			var newMenu = findChildObject(this, 'new');
131
 			var newMenu = findChildObject(this, 'new');
132
 			newMenu.triggered.connect(function() {
132
 			newMenu.triggered.connect(function() {
133
 				createWindow("sampleRoastingBatch");
133
 				createWindow("sampleRoastingBatch");
179
 			stop.clicked.connect(function() {
179
 			stop.clicked.connect(function() {
180
 				submit.setEnabled(true);
180
 				submit.setEnabled(true);
181
 			});
181
 			});
182
-			roastButton.clicked.connect(function() {
182
+                        var validateCapacity = function() {
183
+                            if(checkCapacity == "true") {
184
+                                if(convertToPounds(parseFloat(green.text), GunitBox.currentText) > poundsCapacity) {
185
+                                    return false;
186
+                                }
187
+                            }
188
+                            return true;
189
+                        }
190
+                        roastButton.clicked.connect(function() {
191
+                            var proceed = false;
192
+                            if(validateCapacity()) {
193
+                                proceed = true;
194
+                            } else {
195
+                                proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
196
+                                TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
197
+                            }
198
+                            if(proceed) {
199
+                                doRoast();
200
+                            }
201
+                        });
202
+                        var doRoast = function() {
183
 				var lc = 1;
203
 				var lc = 1;
184
 				currentBatchInfo = batch;
204
 				currentBatchInfo = batch;
185
 				query = new QSqlQuery();
205
 				query = new QSqlQuery();
232
 						input.input();
252
 						input.input();
233
 						graph.updatesEnabled = true;
253
 						graph.updatesEnabled = true;
234
 						log.updatesEnabled = true;
254
 						log.updatesEnabled = true;
235
-						log.newAnnotation("End", 1, lc);
255
+						log.newAnnotation(TTR("sampleRoastingBatch", "End"), 1, lc);
236
 					}
256
 					}
237
 				}
257
 				}
238
 				query = query.invalidate();
258
 				query = query.invalidate();
239
-				var t = "Typica - Sample Roasting: [*]" + name.text;
259
+				var t = TTR("sampleRoastingBatch", "Typica - Sample Roasting: [*]") + name.text;
240
 				if(profileName.currentText != '')
260
 				if(profileName.currentText != '')
241
 				{
261
 				{
242
 					t = t + ", " + profileName.currentText;
262
 					t = t + ", " + profileName.currentText;
243
 				}
263
 				}
244
 				navigationwindow.loggingWindow.windowTitle = t;
264
 				navigationwindow.loggingWindow.windowTitle = t;
245
-			});
265
+			};
246
 			var notes = findChildObject(this, 'annotation');
266
 			var notes = findChildObject(this, 'annotation');
247
 			var machine = findChildObject(this, 'machine');
267
 			var machine = findChildObject(this, 'machine');
248
 			var duration = findChildObject(this, 'duration');
268
 			var duration = findChildObject(this, 'duration');
250
 			var vendor = findChildObject(this, 'vendor');
270
 			var vendor = findChildObject(this, 'vendor');
251
 			var attributes = findChildObject(this, 'attributes');
271
 			var attributes = findChildObject(this, 'attributes');
252
 			var target = findChildObject(this, 'target');
272
 			var target = findChildObject(this, 'target');
253
-			submit.clicked.connect(function() {
273
+                        submit.clicked.connect(function() {
274
+                            var proceed = false;
275
+                            if(validateCapacity()) {
276
+                                proceed = true;
277
+                            } else {
278
+                                proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
279
+                                TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
280
+                            }
281
+                            if(proceed) {
282
+                                doLoadProfile();
283
+                            }
284
+                        });
285
+                        var doSubmit = function() {
254
 				query = new QSqlQuery();
286
 				query = new QSqlQuery();
255
 				query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
287
 				query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
256
 				query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
288
 				query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
314
 				query.exec();
346
 				query.exec();
315
 				query.next();
347
 				query.next();
316
 				var roastedId = query.value(0);
348
 				var roastedId = query.value(0);
317
-				query.prepare("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, :unroastedids, NULL, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
349
+				query.prepare("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, :unroastedids, :greens, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
318
 				query.bind(":time", timefield.text);
350
 				query.bind(":time", timefield.text);
319
 				query.bind(":unroastedids", "{" + greenId + "}");
351
 				query.bind(":unroastedids", "{" + greenId + "}");
352
+                                query.bind(":greens", "{" + convertToPounds(parseFloat(green.text), GunitBox.currentText) + "}");
320
 				query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
353
 				query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
321
 				query.bind(":roastedid", Number(roastedId));
354
 				query.bind(":roastedid", Number(roastedId));
322
 				query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
355
 				query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
327
 				query.exec();
360
 				query.exec();
328
 				query = query.invalidate();
361
 				query = query.invalidate();
329
 				batch.close();
362
 				batch.close();
330
-			});
363
+			}
331
 		]]>
364
 		]]>
332
 	</program>
365
 	</program>
333
 </window>
366
 </window>

+ 8
- 8
config/Windows/offline.xml View File

11
         </splitter>
11
         </splitter>
12
     </layout>
12
     </layout>
13
 	<menu name="File">
13
 	<menu name="File">
14
-		<item id="save" shortcut="Ctrl+S">Save</item>
15
-		<item id="print" shortcut="Ctrl+P">Print</item>
16
-		<item id="export">Export CSV</item>
17
-		<item id="svgexport">Export XHTML+SVG</item>
14
+		<item id="save" shortcut="Ctrl+S">Save...</item>
15
+		<item id="print" shortcut="Ctrl+P">Print...</item>
16
+		<item id="export">Export CSV...</item>
17
+		<item id="svgexport">Export XHTML+SVG...</item>
18
 		<item id="quit" shortcut="Ctrl+Q">Quit</item>
18
 		<item id="quit" shortcut="Ctrl+Q">Quit</item>
19
 	</menu>
19
 	</menu>
20
 	<menu name="Log">
20
 	<menu name="Log">
54
 			};
54
 			};
55
 			var saveMenu = findChildObject(this, 'save');
55
 			var saveMenu = findChildObject(this, 'save');
56
 			saveMenu.triggered.connect(function() {
56
 			saveMenu.triggered.connect(function() {
57
-				var filename = QFileDialog.getSaveFileName(window, "Save Log As…", QSettings.value("script/lastDir", "") + "/");
57
+				var filename = QFileDialog.getSaveFileName(window, TTR("offline", "Save Log As..."), QSettings.value("script/lastDir", "") + "/");
58
 				if(filename != "") {
58
 				if(filename != "") {
59
 					var file = new QFile(filename);
59
 					var file = new QFile(filename);
60
 					setLogOutputColumns();
60
 					setLogOutputColumns();
65
 			var printMenu = findChildObject(this, 'print');
65
 			var printMenu = findChildObject(this, 'print');
66
 			printMenu.triggered.connect(function() {
66
 			printMenu.triggered.connect(function() {
67
 				var exportWindow = createWindow("print");
67
 				var exportWindow = createWindow("print");
68
-				exportWindow.windowTitle = "Typica - Print";
68
+				exportWindow.windowTitle = TTR("offline", "Typica - Print");
69
 				exportWindow.log = log;
69
 				exportWindow.log = log;
70
 			});
70
 			});
71
 			var exportMenu = findChildObject(this, 'export');
71
 			var exportMenu = findChildObject(this, 'export');
72
 			exportMenu.triggered.connect(function() {
72
 			exportMenu.triggered.connect(function() {
73
-				var filename = QFileDialog.getSaveFileName(window, "Export CSV As…", QSettings.value("script/lastDir", "") + "/");
73
+				var filename = QFileDialog.getSaveFileName(window, TTR("offline", "Export CSV As..."), QSettings.value("script/lastDir", "") + "/");
74
 				if(filename != "") {
74
 				if(filename != "") {
75
 					var file = new QFile(filename);
75
 					var file = new QFile(filename);
76
 					setLogOutputColumns();
76
 					setLogOutputColumns();
81
 			var svgExportMenu = findChildObject(this, 'svgexport');
81
 			var svgExportMenu = findChildObject(this, 'svgexport');
82
 			svgExportMenu.triggered.connect(function() {
82
 			svgExportMenu.triggered.connect(function() {
83
 				var exportWindow = createWindow("exportWindow");
83
 				var exportWindow = createWindow("exportWindow");
84
-				exportWindow.windowTitle = "Typica - Export XHTML+SVG";
84
+				exportWindow.windowTitle = TTR("offline", "Typica - Export XHTML+SVG");
85
 				exportWindow.log = log;
85
 				exportWindow.log = log;
86
 			});
86
 			});
87
 			var quitMenu = findChildObject(this, 'quit');
87
 			var quitMenu = findChildObject(this, 'quit');

+ 6
- 6
config/Windows/print.xml View File

279
                 output.writeAttribute("transform", "rotate(-90 40,150)");
279
                 output.writeAttribute("transform", "rotate(-90 40,150)");
280
                 if(log.displayUnits() == 10143)
280
                 if(log.displayUnits() == 10143)
281
                 {
281
                 {
282
-                    output.writeCDATA("Temperature (°C)");
282
+                    output.writeCDATA(TTR("print", "Temperature (°C)"));
283
                 }
283
                 }
284
                 else
284
                 else
285
                 {
285
                 {
286
-                    output.writeCDATA("Temperature (°F)");
286
+                    output.writeCDATA(TTR("print", "Temperature (°F)"));
287
                 }
287
                 }
288
                 output.writeEndElement();
288
                 output.writeEndElement();
289
                 output.writeStartElement("text");
289
                 output.writeStartElement("text");
290
                 output.writeAttribute("x", "1.9in");
290
                 output.writeAttribute("x", "1.9in");
291
                 output.writeAttribute("y", "3.3in");
291
                 output.writeAttribute("y", "3.3in");
292
                 output.writeAttribute("font-size", "12");
292
                 output.writeAttribute("font-size", "12");
293
-                output.writeCDATA("Time (minutes)");
293
+                output.writeCDATA(TTR("print", "Time (minutes)"));
294
                 output.writeEndElement();
294
                 output.writeEndElement();
295
                 output.writeStartElement("line");
295
                 output.writeStartElement("line");
296
                 output.writeAttribute("x1", "0.4in");
296
                 output.writeAttribute("x1", "0.4in");
675
                 if(lossField.text != "")
675
                 if(lossField.text != "")
676
                 {
676
                 {
677
                     output.writeStartElement("p");
677
                     output.writeStartElement("p");
678
-                    output.writeCDATA("Weight loss: ");
678
+                    output.writeCDATA(TTR("print", "Weight loss: "));
679
                     output.writeCDATA(lossField.text);
679
                     output.writeCDATA(lossField.text);
680
                     if(tolField.text != "")
680
                     if(tolField.text != "")
681
                     {
681
                     {
687
                     output.writeStartElement("table");
687
                     output.writeStartElement("table");
688
                     output.writeStartElement("tr");
688
                     output.writeStartElement("tr");
689
                     output.writeStartElement("th");
689
                     output.writeStartElement("th");
690
-                    output.writeCDATA("Roasted");
690
+                    output.writeCDATA(TTR("print", "Roasted"));
691
                     output.writeEndElement();
691
                     output.writeEndElement();
692
                     output.writeStartElement("th");
692
                     output.writeStartElement("th");
693
-                    output.writeCDATA("Green");
693
+                    output.writeCDATA(TTR("print", "Green"));
694
                     output.writeEndElement();
694
                     output.writeEndElement();
695
                     output.writeEndElement();
695
                     output.writeEndElement();
696
                     var green;
696
                     var green;

+ 723
- 705
config/Windows/productionroaster.xml
File diff suppressed because it is too large
View File


+ 4
- 4
config/Windows/roastmanager.xml View File

24
     </layout>
24
     </layout>
25
     <program>
25
     <program>
26
         <![CDATA[
26
         <![CDATA[
27
-            this.displayStatus("Ready.");
27
+            this.displayStatus(TTR("newroasted", "Ready."));
28
             var itemname = findChildObject(this, 'name');
28
             var itemname = findChildObject(this, 'name');
29
             var newItemButton = findChildObject(this, 'ok');
29
             var newItemButton = findChildObject(this, 'ok');
30
             var window = this;
30
             var window = this;
43
                 drop2.clear();
43
                 drop2.clear();
44
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
44
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
45
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
45
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
46
-                window.displayStatus("Item removed.");
46
+                window.displayStatus(TTR("newroasted", "Item removed."));
47
             });
47
             });
48
             restoreButton.clicked.connect(function() {
48
             restoreButton.clicked.connect(function() {
49
                 var q = "INSERT INTO current_items (item) VALUES (:id)";
49
                 var q = "INSERT INTO current_items (item) VALUES (:id)";
56
                 drop2.clear();
56
                 drop2.clear();
57
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
57
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
58
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
58
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
59
-                window.displayStatus("Item restored.");
59
+                window.displayStatus(TTR("newroasted", "Item restored."));
60
             });
60
             });
61
             newItemButton.clicked.connect(function() {
61
             newItemButton.clicked.connect(function() {
62
                 var q = "INSERT INTO items (id, name, reference, unit, quantity, category) VALUES (default, :name, NULL, 'lb', 0, 'Coffee: Roasted') RETURNING id";
62
                 var q = "INSERT INTO items (id, name, reference, unit, quantity, category) VALUES (default, :name, NULL, 'lb', 0, 'Coffee: Roasted') RETURNING id";
76
                 drop2.clear();
76
                 drop2.clear();
77
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
77
                 drop1.addSqlOptions("SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name");
78
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
78
                 drop2.addSqlOptions("SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id NOT IN (SELECT item FROM current_items) ORDER BY name");
79
-                window.displayStatus("Item added.");
79
+                window.displayStatus(TTR("newroasted", "Item added."));
80
             });
80
             });
81
         ]]>
81
         ]]>
82
     </program>
82
     </program>

+ 92
- 0
config/Windows/roastspec.xml View File

1
+<window id="roastspec">
2
+    <layout type="horizontal">
3
+        <layout type="vertical">
4
+            <layout type="horizontal">
5
+                <label>Coffee:</label>
6
+                <sqldrop data="0" display="1" showdata="false" id="currentitems">
7
+                    <query>SELECT id, name FROM items WHERE id IN (SELECT item FROM current_items) ORDER BY name</query>
8
+                </sqldrop>
9
+            </layout>
10
+            <layout type="horizontal">
11
+                <label>Expected % weight loss:</label>
12
+                <line validator="numeric" id="expectedloss" />
13
+            </layout>
14
+            <layout type="horizontal">
15
+                <label>Tolerance</label>
16
+                <line validator="numeric" id="tolerance" />
17
+            </layout>
18
+            <label>Specification Notes:</label>
19
+            <textarea id="notes" />
20
+            <layout type="horizontal">
21
+                <stretch />
22
+                <button id="save" type="push" name="Save" />
23
+            </layout>
24
+        </layout>
25
+    </layout>
26
+    <program>
27
+        <![CDATA[
28
+            var window = this;
29
+            this.windowTitle = TTR("roastspec", "Typica - Edit Roasting Specification");
30
+            var selector = findChildObject(this, 'currentitems');
31
+            var expected = findChildObject(this, 'expectedloss');
32
+            var tolerance = findChildObject(this, 'tolerance');
33
+            var notes = findChildObject(this, 'notes');
34
+            var savebutton = findChildObject(this, 'save');
35
+            var updateDisplay = function() {
36
+                var query = new QSqlQuery();
37
+                query.prepare("SELECT loss, tolerance, notes FROM roasting_specification WHERE item = :id1 AND time = (SELECT max(time) FROM roasting_specification WHERE item = :id2)");
38
+                query.bind(":id1", selector.currentData());
39
+                query.bind(":id2", selector.currentData());
40
+                query.exec();
41
+                if(query.next()) {
42
+                    if(query.value(0).length > 0) {
43
+                        expected.text = Number(query.value(0)) * 100;
44
+                    } else {
45
+                        expected.text = "";
46
+                    }
47
+                    if(query.value(1).length > 0) {
48
+                        tolerance.text = Number(query.value(1)) * 100;
49
+                    } else {
50
+                        tolerance.text = "";
51
+                    }
52
+                    notes.plainText = query.value(2);
53
+                } else {
54
+                    expected.text = "";
55
+                    tolerance.text = "";
56
+                    notes.plainText = "";
57
+                }                
58
+                query = query.invalidate();
59
+            };
60
+            updateDisplay();
61
+            selector['currentIndexChanged(int)'].connect(function() {
62
+                updateDisplay();
63
+            });
64
+            savebutton.clicked.connect(function() {
65
+                var query = new QSqlQuery();
66
+                var columnspec = "time, item, ";
67
+                var valuespec = "'now', :id, ";
68
+                if(expected.text.length > 0) {
69
+                    columnspec += "loss, ";
70
+                    valuespec += ":loss, ";
71
+                }
72
+                if(tolerance.text.length > 0) {
73
+                    columnspec += "tolerance, ";
74
+                    valuespec += ":tolerance, ";
75
+                }
76
+                columnspec += "notes";
77
+                valuespec += ":notes";
78
+                query.prepare("INSERT INTO roasting_specification (" + columnspec + ") VALUES (" + valuespec + ")");
79
+                query.bind(":id", selector.currentData());
80
+                if(expected.text.length > 0) {
81
+                    query.bind(":loss", Number(expected.text) / 100);
82
+                }
83
+                if(tolerance.text.length > 0) {
84
+                    query.bind(":tolerance", Number(tolerance.text) / 100);
85
+                }
86
+                query.bind(":notes", notes.plainText);
87
+                query.exec();
88
+                window.close();
89
+            });
90
+        ]]>
91
+    </program>
92
+</window>

+ 1
- 1
config/Windows/setsampleparameters.xml View File

13
         print("Program loaded");
13
         print("Program loaded");
14
         var window = this;
14
         var window = this;
15
         var instructions = findChildObject(this, 'instructions');
15
         var instructions = findChildObject(this, 'instructions');
16
-        instructions.plainText = "%1 will be replaced with a sample number.";
16
+        instructions.plainText = TTR("sampleParameters", "%1 will be replaced with a sample number.");
17
         instructions.readOnly = true;
17
         instructions.readOnly = true;
18
         var reset = findChildObject(this, 'reset');
18
         var reset = findChildObject(this, 'reset');
19
         reset.clicked.connect(function() {
19
         reset.clicked.connect(function() {

+ 24
- 24
config/config.xml View File

8
     <include src="Windows/navigation.xml" />
8
     <include src="Windows/navigation.xml" />
9
     <include src="Windows/success.xml" />
9
     <include src="Windows/success.xml" />
10
     <include src="Windows/roastmanager.xml" />
10
     <include src="Windows/roastmanager.xml" />
11
-    <include src="Windows/history.xml" />
12
     <include src="Windows/greeninventory.xml" />
11
     <include src="Windows/greeninventory.xml" />
13
     <include src="Windows/newroaster.xml" />
12
     <include src="Windows/newroaster.xml" />
14
     <include src="Windows/batchdetailsnew.xml" />
13
     <include src="Windows/batchdetailsnew.xml" />
23
     <include src="Windows/cuppingsessionsummary.xml" />
22
     <include src="Windows/cuppingsessionsummary.xml" />
24
     <include src="Windows/cuppingsummary.xml" />
23
     <include src="Windows/cuppingsummary.xml" />
25
     <include src="Windows/purchase.xml" />
24
     <include src="Windows/purchase.xml" />
26
-	<include src="Windows/invoicelist.xml" />
27
-	<include src="Windows/invoiceinfo.xml" />
28
-	<include src="Windows/editinvoice.xml" />
29
-	<include src="Windows/editinvoiceitem.xml" />
30
-	<include src="Windows/editfee.xml" />
31
-	<include src="Windows/optime.xml" />
32
-	<include src="Windows/greensales.xml" />
33
-	<include src="Windows/profilehistory.xml" />
34
-	<include src="Windows/editbatchdetails.xml" />
35
-	<include src="Windows/newsamplebatch.xml" />
36
-	<program>
37
-		<![CDATA[
38
-		Windows = new Object();
39
-		var loggingWindow;
40
-		var currentBatchInfo;
41
-		var navwindow = createWindow("navwindow");
42
-		var aadapt;
43
-		var badapt;
44
-		var azero;
45
-		var bzero;
46
-		navwindow.windowTitle = "Typica - Choose Your Path";
47
-		]]>
48
-	</program>
25
+    <include src="Windows/invoiceinfo.xml" />
26
+    <include src="Windows/editinvoice.xml" />
27
+    <include src="Windows/editinvoiceitem.xml" />
28
+    <include src="Windows/editfee.xml" />
29
+    <include src="Windows/optime.xml" />
30
+    <include src="Windows/greensales.xml" />
31
+    <include src="Windows/profilehistory.xml" />
32
+    <include src="Windows/editbatchdetails.xml" />
33
+    <include src="Windows/newsamplebatch.xml" />
34
+    <include src="Windows/editreminder.xml" />
35
+    <include src="Windows/roastspec.xml" />
36
+    <program>
37
+        <![CDATA[
38
+            Windows = new Object();
39
+            var loggingWindow;
40
+            var currentBatchInfo;
41
+            var navwindow = createWindow("navwindow");
42
+            var aadapt;
43
+            var badapt;
44
+            var azero;
45
+            var bzero;
46
+            navwindow.windowTitle = TTR("config", "Typica - Choose Your Path");
47
+        ]]>
48
+    </program>
49
 </application>
49
 </application>

+ 0
- 1
docs/documentation.html View File

16
 					<a class="tab" href="downloads.html" >Downloads</a>
16
 					<a class="tab" href="downloads.html" >Downloads</a>
17
 					<a class="tab active" >Documentation</a>
17
 					<a class="tab active" >Documentation</a>
18
 					<a class="tab" href="screenshots.html" >Screenshots and Videos</a>
18
 					<a class="tab" href="screenshots.html" >Screenshots and Videos</a>
19
-					<a class="tab" href="involvement.html" >Get Involved</a>
20
 					<a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
19
 					<a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
21
 				</div>
20
 				</div>
22
 			</div>
21
 			</div>

+ 4
- 8
docs/documentation/part2.html View File

16
 					<a class="tab" href="../downloads.html" >Downloads</a>
16
 					<a class="tab" href="../downloads.html" >Downloads</a>
17
 					<a class="tab active" href="../documentation.html" >Documentation</a>
17
 					<a class="tab active" href="../documentation.html" >Documentation</a>
18
 					<a class="tab" href="../screenshots.html" >Screenshots and Videos</a>
18
 					<a class="tab" href="../screenshots.html" >Screenshots and Videos</a>
19
-					<a class="tab" href="../involvement.html" >Get Involved</a>
20
 					<a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
19
 					<a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
21
 				</div>
20
 				</div>
22
 			</div>
21
 			</div>
27
 				<ul>
26
 				<ul>
28
 					<li><a href="part2/application.html">Opening the Application</a></li>
27
 					<li><a href="part2/application.html">Opening the Application</a></li>
29
 					<ul>
28
 					<ul>
30
-						<li><a href="part2/database-connection.html">Database Connection Settings</a></li>
31
-						<li><a href="part2/open-configuration.html">Open Configuration File</a></li>
29
+						<li><a href="windowreference/databaseconnection.html">Connecting to a database</a></li>
30
+						<li><a href="windowreference/openconfigurationfile">Open Configuration File</a></li>
32
 						<ul>
31
 						<ul>
33
 							<li><a href="part2/argument-c.html">Eliminating the Configuration Prompt</a></li>
32
 							<li><a href="part2/argument-c.html">Eliminating the Configuration Prompt</a></li>
34
 						</ul>
33
 						</ul>
35
-						<li><a>Choose Your Path</a></li>
34
+						<li><a href="windowreference/navigation.html">Main navigation window</a></li>
36
 					</ul>
35
 					</ul>
37
 					<li><a>Configure Roasters</a></li>
36
 					<li><a>Configure Roasters</a></li>
38
-					<li><a>Roast Coffee</a></li>
37
+					<li><a href="windowreference/logging.html">The Logging View</a></li>
39
 					<li><a>Purchase Green Coffee</a></li>
38
 					<li><a>Purchase Green Coffee</a></li>
40
 					<li><a>Manage Roasted Coffee Items</a></li>
39
 					<li><a>Manage Roasted Coffee Items</a></li>
41
 					<li><a>Update Inventory</a></li>
40
 					<li><a>Update Inventory</a></li>
42
-					<li><a>Batch Log</a></li>
43
 					<li><a>Cupping</a></li>
41
 					<li><a>Cupping</a></li>
44
 					<li><a>Import Target Roast Profiles</a></li>
42
 					<li><a>Import Target Roast Profiles</a></li>
45
-					<li><a>Invoice List</a></li>
46
 					<li><a>Enter Green Coffee Sales</a></li>
43
 					<li><a>Enter Green Coffee Sales</a></li>
47
-					<li><a>Reports</a></li>
48
 				</ul>
44
 				</ul>
49
 			</div>
45
 			</div>
50
 		</div>
46
 		</div>

+ 79
- 0
docs/documentation/windowreference/databaseconnection.html View File

1
+<html>
2
+    <head>
3
+        <title>Typica - Connecting to a database</title>
4
+        <link rel="stylesheet" type="text/css" href="../../style.css">
5
+    </head>
6
+    <body>
7
+        <div id="page">
8
+            <div id="topmatter">
9
+                <div id="topbanner">
10
+                    <img src="../../logo96.png" height="96px" width="96px" alt="Typica logo" />
11
+                    <h1>Typica</h1>
12
+                    <h2>Data for Coffee Roasters</h2>
13
+                </div>
14
+                <div id="menu">
15
+                    <a class="tab" href="../../index.html">Project Home</a>
16
+                    <a class="tab" href="../../downloads.html" >Downloads</a>
17
+                    <a class="tab active" >Documentation</a>
18
+                    <a class="tab" href="../../screenshots.html" >Screenshots and Videos</a>
19
+                    <a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
20
+                </div>
21
+            </div>
22
+            <div id="maintext">
23
+                <h1>Connecting to a database</h1>
24
+                <p>When opening Typica, if you have not previously connected to
25
+                a database, you will be prompted for information needed to
26
+                connect to that database.</p>
27
+
28
+                <img src="databaseconnection.png"/>
29
+                
30
+                <p>If you do not want to connect to a database, you can click
31
+                the Cancel button, but doing so means that most of the features
32
+                of Typica will not work. Clicking the Connect button will cause
33
+                Typica to attempt to connect to the database with the
34
+                information provided in the fields. An error message will be
35
+                presented if that connection attempt fails.</p>
36
+                
37
+                <p>After either successfully connecting to a database or
38
+                cancelling, you will be prompted to
39
+                <a href="openconfigurationfile.html">open a configuration
40
+                file.</a></p>
41
+                
42
+                <h2>Field descriptions</h2>
43
+                
44
+                <h3>Database driver</h3>
45
+                <p>Currently the only database supported is PostgreSQL.</p>
46
+                
47
+                <h3>Host name</h3>
48
+                <p>This identifies the computer the database is installed on.
49
+                If the database is on the same computer as Typica, this can be
50
+                <tt>localhost</tt> otherwise it will most likely be an IP
51
+                address for another computer on your network.</p>
52
+                
53
+                <h3>Port number</h3>
54
+                <p>PostgreSQL normally communicates on port 5432 but if you
55
+                have PostgreSQL configured to communicate over a different port
56
+                this field should be changed to match.</p>
57
+                
58
+                <h3>Database name</h3>
59
+                <p>During installation, the PostgreSQL installer creates a
60
+                database called <tt>postgres</tt>. If you've created a new
61
+                database for use with Typica, its name should be used.</p>
62
+                
63
+                <h3>User name</h3>
64
+                <p>During installation, the PostgreSQL installer creates a
65
+                database user called <tt>postgres</tt>. If you don't want to
66
+                create separate users, this account can be used. Typica keeps
67
+                track of which database user performed many data entry tasks so
68
+                it is recommended to create new users for everybody who will be
69
+                using Typica.</p>
70
+                
71
+                <h3>Password</h3>
72
+                <p>This is the password previously set for the database user.
73
+                If you are using the <tt>postgres</tt> user, this was set
74
+                during PostgreSQL installation.</p>
75
+
76
+            </div>
77
+        </div>
78
+    </body>
79
+</html>

BIN
docs/documentation/windowreference/databaseconnection.png View File


BIN
docs/documentation/windowreference/databasemenu.png View File


BIN
docs/documentation/windowreference/inventoryreports.png View File


+ 202
- 0
docs/documentation/windowreference/logging.html View File

1
+<html>
2
+    <head>
3
+        <title>Typica - The Logging View</title>
4
+        <link rel="stylesheet" type="text/css" href="../../style.css">
5
+    </head>
6
+    <body>
7
+        <div id="page">
8
+            <div id="topmatter">
9
+                <div id="topbanner">
10
+                    <img src="../../logo96.png" height="96px" width="96px" alt="Typica logo" />
11
+                    <h1>Typica</h1>
12
+                    <h2>Data for Coffee Roasters</h2>
13
+                </div>
14
+                <div id="menu">
15
+                    <a class="tab" href="../../index.html">Project Home</a>
16
+                    <a class="tab" href="../../downloads.html" >Downloads</a>
17
+                    <a class="tab active" >Documentation</a>
18
+                    <a class="tab" href="../../screenshots.html" >Screenshots and Videos</a>
19
+                    <a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
20
+                </div>
21
+            </div>
22
+            <div id="maintext">
23
+                <h1>The Logging View</h1>
24
+                <p>The appearance of the logging view depends on how the selected
25
+                roaster was configured, but there are a few main areas where
26
+                different interface items are located.</p>
27
+
28
+                <img src="logging.png"/>
29
+                
30
+                <p>The window title will change to reflect a loaded target roast
31
+                profile and can provide the name of the coffee being roasted.</p>
32
+
33
+                <p>The menu bar provides access to a number of operations such
34
+                as saving or loading data on disk, entering new batch information,
35
+                clearing recorded data, changing how data is displayed, and more.
36
+                Note that on a Mac the menu bar will be at the top of the screen
37
+                and not within the window as is normal on that platform.</p>
38
+
39
+                <p>Continuing down the window is the indicator panel. This is
40
+                where current temperature indicators, rate of change information,
41
+                and timers will be displayed. The number, type, and order of
42
+                indicators depends on the roaster configuration. There are
43
+                splitter handles between each indicator which can be used to
44
+                control the width of each indicator. The last indicator should
45
+                always be the batch timer. If you don't see this, there should
46
+                be a splitter handle to the right of the last visible indicator
47
+                which can be dragged left to reveal additional indicators. A
48
+                splitter handle at the bottom can be used to control the height
49
+                of all indicators.</p>
50
+
51
+                <p>Next is a row of buttons. At minimum this will include buttons
52
+                for starting and stopping the batch, but a number of other
53
+                controls for inserting notes in the log can also be configured
54
+                to appear here. Under these buttons is another splitter handle
55
+                that can be used to control the height of to table and graph
56
+                below.</p>
57
+
58
+                <p>Below this and on the left is a table view showing times,
59
+                temperatures, and notes for both a target profile if one is
60
+                loaded and the current batch if one is being recorded.</p>
61
+
62
+                <p>To the right of the table view is a graph showing both
63
+                loaded target profiles, if any, and current batch data.</p>
64
+
65
+                <h2>Buttons</h2>
66
+
67
+                <h3>Start Batch</h3>
68
+                <p>This button starts recording roasting data. If the New Batch
69
+                or New Sample Batch windows were used, the roasting data can be
70
+                associated with a batch in the database. Clicking this will
71
+                also start the batch timer from 00:00, start any other timers
72
+                that have been configured to start from the start of the batch,
73
+                and create any annotations that have been configured for entry
74
+                at the start of the batch if any.</p>
75
+
76
+                <h3>Stop Batch</h3>
77
+                <p>This button stops the batch timer, stops recording roasting
78
+                data, and if the New Batch or New Sample Batch windows were
79
+                used, the window with data associated with the finished batch
80
+                will be raised.</p>
81
+
82
+                <h3>Additional Buttons</h3>
83
+                <p>Additional buttons and controls will appear if these have
84
+                been configured.</p>
85
+
86
+                <h2>The File Menu</h2>
87
+
88
+                <img src="loggingfile.png" />
89
+
90
+                <h3>Open...</h3>
91
+                <p>This can be used to load roasting data previously saved to
92
+                disk. This is mainly useful when receiving roasting data that
93
+                has been shared from another roaster. In normal use it is much
94
+                better to use information saved to the database instead of
95
+                managing files manually.</p>
96
+
97
+                <h3>Save...</h3>
98
+                <p>This can be used to save roasting data to disk. This is
99
+                mainly useful to share roasting data with another person using
100
+                Typica. For other uses it is much better to use the New Batch
101
+                and New Sample Batch windows to have roasting data saved to the
102
+                database.</p>
103
+
104
+                <h3>Print...</h3>
105
+                <p>This can be used to print roasting data. The print window
106
+                will appear, allowing the information printed to be
107
+                customized.</p>
108
+
109
+                <h3>Export CSV...</h3>
110
+                <p>This can be used to export roasting data as a CSV file which
111
+                can be opened in all popular spreadsheet applications or used
112
+                with a wide variety of other tools. If this is something that
113
+                you frequently require, it may be a good idea to reach out to
114
+                the author with your use case and see if there is a way to do
115
+                what you want within Typica or if Typica should be extended to
116
+                support your use case.</p>
117
+
118
+                <h3>Export XHTML+SVG</h3>
119
+                <p>This can be used to produce the same information that can be
120
+                printed, but produces that as an XHTML+SVG document.</p>
121
+
122
+                <h3>Quit</h3>
123
+                <p>On some platforms this might be called Exit and on the Mac
124
+                this will be moved under the Typica menu. It is used to quit
125
+                Typica.</p>
126
+                
127
+                <h2>The Batch Menu</h2>
128
+                
129
+                <img src="loggingbatch.png" />
130
+                
131
+                <h3>New Batch...</h3>
132
+                <p>This is the preferred way to enter the details of a
133
+                production roast such that recorded roasting data can be
134
+                associated with a batch and green coffee inventory can be
135
+                adjusted. Selecting this will open the New Batch window.</p>
136
+                
137
+                <h3>New Sample Batch...</h3>
138
+                <p>When roasting a green coffee not in inventory such as
139
+                pre-purchase samples, you can use this menu item to open the
140
+                New Sample Batch window and associate the roasting data with
141
+                additional information about the coffee.</p>
142
+                
143
+                <h3>Load Additional Profiles...</h3>
144
+                <p>If you do not have a target roast profile for the roasted
145
+                coffee item you are producing but have a batch that you would
146
+                like to have available for reference previously saved in the
147
+                database, you can use this menu item to load that information
148
+                as a target roast profile.</p>
149
+                
150
+                <h2>The Log Menu</h2>
151
+                
152
+                <img src="logginglog.png" />
153
+                
154
+                <h3>Display Celsius</h3>
155
+                <p>This menu item can be used to change displayed temperature
156
+                measurements to Celsius.</p>
157
+                
158
+                <h3>Display Fahrenheit</h3>
159
+                <p>This menu item can be used to change displayed temperature
160
+                measurements to Fahrenheit.</p>
161
+                
162
+                <h3>New Sample Parameters</h3>
163
+                <p>If a Counting Button has been configured, this menu item
164
+                will be available to change the annotation text or reset the
165
+                number used in annotations when that button is activated.</p>
166
+                
167
+                <h3>Clear Log</h3>
168
+                <p>This menu item removes all information currently visible in
169
+                the table and graph views.</p>
170
+                
171
+                <h3>Millisecond View</h3>
172
+                <h3>1 Second View</h3>
173
+                <h3>5 Second View</h3>
174
+                <h3>10 Second View</h3>
175
+                <h3>15 Second View</h3>
176
+                <h3>30 Second View</h3>
177
+                <h3>1 Minute View</h3>
178
+                
179
+                <p>These items affect the table view. The millisecond view will
180
+                show every measurement in the table while the other views will
181
+                limit the entries shown to one per indicated interval with the
182
+                exception of times associated with an annotation which will
183
+                always be shown regardless of the selected view.</p>
184
+                
185
+                <h3>Manual Entry</h3>
186
+                <p>This can be used for entering roasting data collected when
187
+                not connected to a roaster. The utility of this feature is
188
+                somewhat limited and generally not worth using.</p>
189
+                
190
+                <h2> The Graph Menu</h2>
191
+                
192
+                <img src="logginggraph.png" />
193
+                
194
+                <h3>Reset Translation</h3>
195
+                <p>If the roaster in use has been configured to use roast
196
+                profile translation, this item can be used to reset any
197
+                currently applied translation transformation in the graph.</p>
198
+                
199
+            </div>
200
+        </div>
201
+    </body>
202
+</html>

BIN
docs/documentation/windowreference/logging.png View File


BIN
docs/documentation/windowreference/loggingbatch.png View File


BIN
docs/documentation/windowreference/loggingfile.png View File


BIN
docs/documentation/windowreference/logginggraph.png View File


BIN
docs/documentation/windowreference/logginglog.png View File


+ 156
- 0
docs/documentation/windowreference/navigation.html View File

1
+<html>
2
+    <head>
3
+        <title>Typica - Choose Your Path</title>
4
+        <link rel="stylesheet" type="text/css" href="../../style.css">
5
+    </head>
6
+    <body>
7
+        <div id="page">
8
+            <div id="topmatter">
9
+                <div id="topbanner">
10
+                    <img src="../../logo96.png" height="96px" width="96px" alt="Typica logo" />
11
+                    <h1>Typica</h1>
12
+                    <h2>Data for Coffee Roasters</h2>
13
+                </div>
14
+                <div id="menu">
15
+                    <a class="tab" href="../../index.html">Project Home</a>
16
+                    <a class="tab" href="../../downloads.html" >Downloads</a>
17
+                    <a class="tab active" >Documentation</a>
18
+                    <a class="tab" href="../../screenshots.html" >Screenshots and Videos</a>
19
+                    <a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
20
+                </div>
21
+            </div>
22
+            <div id="maintext">
23
+                <h1>Main navigation window</h1>
24
+                <p>Most of the functionality in Typica is available through the
25
+                main navigation window.</p>
26
+
27
+                <img src="navigation.png"/>
28
+                
29
+                <h2>Buttons</h2>
30
+                
31
+                <h3>Configure Roasters</h3>
32
+                
33
+                <p>Typica can communicate with different pieces of data
34
+                acquisition hardware which must be configured before use.
35
+                Various features related to recording roasting data can be
36
+                set in the configuration
37
+                window.
38
+                
39
+                <h3>Roast Coffee</h3>
40
+                
41
+                <p>Once at least one coffee roaster has been configured, it
42
+                will be available from the selector next to the Roast Coffee
43
+                button. When you want to roast coffee, select the machine
44
+                you'll be using and click the Roast Coffee button. This will
45
+                take you to <a href="logging.html">the logging view.</a></p>
46
+                
47
+                <h3>Purchase Green Coffee</h3>
48
+                
49
+                <p>Typica tracks green coffee inventory. This button allows
50
+                you to enter the details of green coffee purchases.</p>
51
+                
52
+                <h3>Manage Roasted Coffee Items</h3>
53
+                
54
+                <p>When entering the details of a new production batch, you
55
+                will want to select a roasted coffee item. This is connected
56
+                to the green coffee(s) used, a target roast profile and
57
+                roasting specification if set. This button allows you to create
58
+                new roasted coffee items, discontinue items so that they will
59
+                not show up in lists, and bring back old items.</p>
60
+                
61
+                <h3>Edit Roasting Specification</h3>
62
+                
63
+                <p>Expected percent weight loss and other information can be
64
+                presented when roasting coffee for use in determining if a
65
+                batch matches its product specification.</p>
66
+                
67
+                <h3>Update Inventory</h3>
68
+                
69
+                <p>Inventory and loss transactions can be used to adjust
70
+                green coffee inventory.</p>
71
+                
72
+                <h3>New Cupping Session</h3>
73
+                <h3>Join Cupping Session</h3>
74
+                <h3>Summarize Cupping Session</h3>
75
+                
76
+                <p>Typica includes a set of cupping features. Some people use
77
+                this, but the author does not recommend it. Something much
78
+                better will be available in the future.</p>
79
+                
80
+                <h3>View Target Roast Profiles</h3>
81
+                
82
+                <p>Any data ever saved as a target roast profile for roasted
83
+                coffee items can be seen here.</p>
84
+                
85
+                <h3>Import Target Roast Profiles</h3>
86
+                
87
+                <p>It is possible to save batch data to disk instead of or in
88
+                addition to saving that to the database. These files can be
89
+                shared with other people using Typica. If you want to set data
90
+                from one of these files as a target roast profile for a given
91
+                roasted coffee item, this is where you do that.
92
+                
93
+                <h3>Enter Green Coffee Sales</h3>
94
+                
95
+                <p>If you sell green coffee, the inventory can be adjusted to
96
+                reflect that here.</p>
97
+                
98
+                <h2>Reports Menu</h2>
99
+                
100
+                <img src="reportsmenu.png" />
101
+                
102
+                <h3>Production</h3>
103
+                
104
+                <p>Production reports present information related to coffee
105
+                roasting activity.</p>
106
+                
107
+                <img src="productionreports.png" />
108
+                
109
+                <h3>Average Use and Cost by Origin</h3>
110
+                <h3>Previous Year Production Comparison</h3>
111
+                <h3>Cost of Green Coffee for Roasted Coffee</h3>
112
+                <h3>Daily Production Report (Detailed)</h3>
113
+                <h3>Batch Log</h3>
114
+                <h3>Previous Year Production Comparison By Month</h3>
115
+                <h3>Production Summary</h3>
116
+                <h3>Reminders</h3>
117
+                <h3>Recent Average Coffee Production</h3>
118
+                
119
+                <h3>Purchase</h3>
120
+                
121
+                <p>Purchase reports present information related to green coffee
122
+                purchases.</p>
123
+                
124
+                <img src="purchasereports.png" />
125
+                
126
+                <h3>Coffee Purchase Previous Years Comparison</h3>
127
+                <h3>Invoices</h3>
128
+                
129
+                <h3>Sales</h3>
130
+                
131
+                <p>Sales reports present information related to sales.</p>
132
+                
133
+                <img src="salesreport.png" />
134
+                
135
+                <h3>Green Coffee Sales</h3>
136
+                
137
+                <h3>Inventory</h3>
138
+                
139
+                <p>Inventory reports present information on inventory.</p>
140
+                
141
+                <img src="inventoryreports.png" />
142
+                
143
+                <h3>Inventory Change Summary</h3>
144
+                <h3>Current Inventory and Availability Projection</h3>
145
+                <h3>Item Transactions</h3>
146
+                
147
+                <h2>Database Menu</h2>
148
+                <h3>Forget Connection Details</h3>
149
+                <p>This menu item can be used to force Typica to prompt for
150
+                database connection information the next time Typica is
151
+                opened.</p>
152
+                
153
+            </div>
154
+        </div>
155
+    </body>
156
+</html>

BIN
docs/documentation/windowreference/navigation.png View File


+ 38
- 0
docs/documentation/windowreference/openconfigurationfile.html View File

1
+<html>
2
+    <head>
3
+        <title>Typica - Open Configuration File</title>
4
+        <link rel="stylesheet" type="text/css" href="../../style.css">
5
+    </head>
6
+    <body>
7
+        <div id="page">
8
+            <div id="topmatter">
9
+                <div id="topbanner">
10
+                    <img src="../../logo96.png" height="96px" width="96px" alt="Typica logo" />
11
+                    <h1>Typica</h1>
12
+                    <h2>Data for Coffee Roasters</h2>
13
+                </div>
14
+                <div id="menu">
15
+                    <a class="tab" href="../../index.html">Project Home</a>
16
+                    <a class="tab" href="../../downloads.html" >Downloads</a>
17
+                    <a class="tab active" >Documentation</a>
18
+                    <a class="tab" href="../../screenshots.html" >Screenshots and Videos</a>
19
+                    <a href="http://appliedcoffeetechnology.tumblr.com/tagged/Typica" class="tab">Blog</a>
20
+                </div>
21
+            </div>
22
+            <div id="maintext">
23
+                <h1>Open Configuration File</h1>
24
+                <p>Most of Typica's appearance and functionality is controlled
25
+                by a set of configuration files. This documentation covers an
26
+                example configuration included with Typica. When prompted to
27
+                open a configuration file, select the config.xml file included
28
+                with Typica. When switching to a new version of Typica, it is
29
+                important to use the configuration file included with the new
30
+                version and not the old one. Opening that file will open the
31
+                <a href="navigation.html">main navigation window.</a></p>
32
+
33
+                <img src="openconfigurationfile.png"/>
34
+                
35
+            </div>
36
+        </div>
37
+    </body>
38
+</html>

BIN
docs/documentation/windowreference/openconfigurationfile.png View File


BIN
docs/documentation/windowreference/productionreports.png View File


BIN
docs/documentation/windowreference/purchasereports.png View File


BIN
docs/documentation/windowreference/reportsmenu.png View File


BIN
docs/documentation/windowreference/salesreport.png View File


+ 1396
- 0
src/Translations/Typica.ts
File diff suppressed because it is too large
View File


BIN
src/Translations/Typica_de.qm View File


+ 1396
- 0
src/Translations/Typica_de.ts
File diff suppressed because it is too large
View File


+ 3
- 0
src/Typica.pro View File

8
 QT += xmlpatterns
8
 QT += xmlpatterns
9
 QT += scripttools
9
 QT += scripttools
10
 QT += webkit
10
 QT += webkit
11
+QT += svg
11
 
12
 
12
 CONFIG += extserialport
13
 CONFIG += extserialport
13
 
14
 
43
 RC_FILE = typica.rc
44
 RC_FILE = typica.rc
44
 ICON = resources/icons/appicons/logo.icns
45
 ICON = resources/icons/appicons/logo.icns
45
 QMAKE_INFO_PLIST = resources/Info.plist
46
 QMAKE_INFO_PLIST = resources/Info.plist
47
+
48
+CODECFORTR = UTF-8

+ 5
- 5
src/abouttypica.cpp View File

1
-/*265:*/
1
+/*275:*/
2
 #line 33 "./abouttypica.w"
2
 #line 33 "./abouttypica.w"
3
 
3
 
4
 #include "abouttypica.h"
4
 #include "abouttypica.h"
5
 
5
 
6
-/*266:*/
6
+/*276:*/
7
 #line 42 "./abouttypica.w"
7
 #line 42 "./abouttypica.w"
8
 
8
 
9
 AboutTypica::AboutTypica():QMainWindow(NULL)
9
 AboutTypica::AboutTypica():QMainWindow(NULL)
17
 setCentralWidget(banner);
17
 setCentralWidget(banner);
18
 }
18
 }
19
 
19
 
20
-#line 6404 "./typica.w"
20
+#line 6592 "./typica.w"
21
 
21
 
22
-/*:266*/
22
+/*:276*/
23
 #line 36 "./abouttypica.w"
23
 #line 36 "./abouttypica.w"
24
 
24
 
25
 
25
 
26
-/*:265*/
26
+/*:275*/

+ 2
- 2
src/abouttypica.h View File

1
-/*264:*/
1
+/*274:*/
2
 #line 14 "./abouttypica.w"
2
 #line 14 "./abouttypica.w"
3
 
3
 
4
 #include <QMainWindow> 
4
 #include <QMainWindow> 
17
 
17
 
18
 #endif
18
 #endif
19
 
19
 
20
-/*:264*/
20
+/*:274*/

+ 20
- 20
src/daterangeselector.cpp View File

1
-/*656:*/
1
+/*666:*/
2
 #line 70 "./daterangeselector.w"
2
 #line 70 "./daterangeselector.w"
3
 
3
 
4
 #include <QCalendarWidget> 
4
 #include <QCalendarWidget> 
11
 
11
 
12
 #include "daterangeselector.h"
12
 #include "daterangeselector.h"
13
 
13
 
14
-/*658:*/
14
+/*668:*/
15
 #line 115 "./daterangeselector.w"
15
 #line 115 "./daterangeselector.w"
16
 
16
 
17
 CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
17
 CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
55
 setLayout(outerLayout);
55
 setLayout(outerLayout);
56
 }
56
 }
57
 
57
 
58
-/*:658*//*659:*/
58
+/*:668*//*669:*/
59
 #line 163 "./daterangeselector.w"
59
 #line 163 "./daterangeselector.w"
60
 
60
 
61
 void CustomDateRangePopup::hideEvent(QHideEvent*)
61
 void CustomDateRangePopup::hideEvent(QHideEvent*)
63
 emit hidingPopup();
63
 emit hidingPopup();
64
 }
64
 }
65
 
65
 
66
-/*:659*//*660:*/
66
+/*:669*//*670:*/
67
 #line 172 "./daterangeselector.w"
67
 #line 172 "./daterangeselector.w"
68
 
68
 
69
 void CustomDateRangePopup::applyRange()
69
 void CustomDateRangePopup::applyRange()
78
 hide();
78
 hide();
79
 }
79
 }
80
 
80
 
81
-/*:660*//*661:*/
81
+/*:670*//*671:*/
82
 #line 189 "./daterangeselector.w"
82
 #line 189 "./daterangeselector.w"
83
 
83
 
84
 void CustomDateRangePopup::validateRange()
84
 void CustomDateRangePopup::validateRange()
93
 }
93
 }
94
 }
94
 }
95
 
95
 
96
-/*:661*/
96
+/*:671*/
97
 #line 81 "./daterangeselector.w"
97
 #line 81 "./daterangeselector.w"
98
 
98
 
99
-/*662:*/
99
+/*672:*/
100
 #line 207 "./daterangeselector.w"
100
 #line 207 "./daterangeselector.w"
101
 
101
 
102
 DateRangeSelector::DateRangeSelector(QWidget*parent):
102
 DateRangeSelector::DateRangeSelector(QWidget*parent):
108
 QDate currentDate= QDate::currentDate();
108
 QDate currentDate= QDate::currentDate();
109
 
109
 
110
 QHBoxLayout*layout= new QHBoxLayout;
110
 QHBoxLayout*layout= new QHBoxLayout;
111
-/*663:*/
111
+/*673:*/
112
 #line 236 "./daterangeselector.w"
112
 #line 236 "./daterangeselector.w"
113
 
113
 
114
 quickSelector->addItem("Yesterday",QVariant(QStringList()<<
114
 quickSelector->addItem("Yesterday",QVariant(QStringList()<<
188
 quickSelector->addItem("Lifetime");
188
 quickSelector->addItem("Lifetime");
189
 quickSelector->addItem("Custom");
189
 quickSelector->addItem("Custom");
190
 
190
 
191
-/*:663*/
191
+/*:673*/
192
 #line 217 "./daterangeselector.w"
192
 #line 217 "./daterangeselector.w"
193
 
193
 
194
 QToolButton*customButton= new QToolButton;
194
 QToolButton*customButton= new QToolButton;
201
 connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
201
 connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
202
 }
202
 }
203
 
203
 
204
-/*:662*//*664:*/
204
+/*:672*//*674:*/
205
 #line 319 "./daterangeselector.w"
205
 #line 319 "./daterangeselector.w"
206
 
206
 
207
 void DateRangeSelector::updateRange(int index)
207
 void DateRangeSelector::updateRange(int index)
217
 }
217
 }
218
 }
218
 }
219
 
219
 
220
-/*:664*//*665:*/
220
+/*:674*//*675:*/
221
 #line 336 "./daterangeselector.w"
221
 #line 336 "./daterangeselector.w"
222
 
222
 
223
 void DateRangeSelector::popupHidden()
223
 void DateRangeSelector::popupHidden()
227
 quickSelector->setCurrentIndex(lastIndex);
227
 quickSelector->setCurrentIndex(lastIndex);
228
 }
228
 }
229
 
229
 
230
-/*:665*//*666:*/
230
+/*:675*//*676:*/
231
 #line 347 "./daterangeselector.w"
231
 #line 347 "./daterangeselector.w"
232
 
232
 
233
 void DateRangeSelector::setCustomRange(QVariant range)
233
 void DateRangeSelector::setCustomRange(QVariant range)
238
 quickSelector->setCurrentIndex(lastIndex);
238
 quickSelector->setCurrentIndex(lastIndex);
239
 }
239
 }
240
 
240
 
241
-/*:666*//*667:*/
241
+/*:676*//*677:*/
242
 #line 362 "./daterangeselector.w"
242
 #line 362 "./daterangeselector.w"
243
 
243
 
244
 void DateRangeSelector::toggleCustom()
244
 void DateRangeSelector::toggleCustom()
279
 }
279
 }
280
 }
280
 }
281
 
281
 
282
-/*:667*//*668:*/
282
+/*:677*//*678:*/
283
 #line 404 "./daterangeselector.w"
283
 #line 404 "./daterangeselector.w"
284
 
284
 
285
 QVariant DateRangeSelector::currentRange()
285
 QVariant DateRangeSelector::currentRange()
287
 return quickSelector->itemData(lastIndex);
287
 return quickSelector->itemData(lastIndex);
288
 }
288
 }
289
 
289
 
290
-/*:668*//*669:*/
290
+/*:678*//*679:*/
291
 #line 412 "./daterangeselector.w"
291
 #line 412 "./daterangeselector.w"
292
 
292
 
293
 void DateRangeSelector::setCurrentIndex(int index)
293
 void DateRangeSelector::setCurrentIndex(int index)
300
 return quickSelector->currentIndex();
300
 return quickSelector->currentIndex();
301
 }
301
 }
302
 
302
 
303
-/*:669*//*670:*/
303
+/*:679*//*680:*/
304
 #line 432 "./daterangeselector.w"
304
 #line 432 "./daterangeselector.w"
305
 
305
 
306
 void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
306
 void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
309
 QVariant(QStringList()<<startDate<<endDate));
309
 QVariant(QStringList()<<startDate<<endDate));
310
 }
310
 }
311
 
311
 
312
-/*:670*//*671:*/
312
+/*:680*//*681:*/
313
 #line 442 "./daterangeselector.w"
313
 #line 442 "./daterangeselector.w"
314
 
314
 
315
 void DateRangeSelector::removeIndex(int index)
315
 void DateRangeSelector::removeIndex(int index)
317
 quickSelector->removeItem(index);
317
 quickSelector->removeItem(index);
318
 }
318
 }
319
 
319
 
320
-/*:671*/
320
+/*:681*/
321
 #line 82 "./daterangeselector.w"
321
 #line 82 "./daterangeselector.w"
322
 
322
 
323
 
323
 
324
-#if 0
324
+#ifdef __unix__
325
 #include "moc_daterangeselector.cpp"
325
 #include "moc_daterangeselector.cpp"
326
 #endif
326
 #endif
327
 
327
 
328
-/*:656*/
328
+/*:666*/

+ 4
- 4
src/daterangeselector.h View File

1
-/*655:*/
1
+/*665:*/
2
 #line 30 "./daterangeselector.w"
2
 #line 30 "./daterangeselector.w"
3
 
3
 
4
 
4
 
9
 #ifndef TypicaDateRangeSelectorHeader
9
 #ifndef TypicaDateRangeSelectorHeader
10
 #define TypicaDateRangeSelectorHeader
10
 #define TypicaDateRangeSelectorHeader
11
 
11
 
12
-/*657:*/
12
+/*667:*/
13
 #line 91 "./daterangeselector.w"
13
 #line 91 "./daterangeselector.w"
14
 
14
 
15
 class CustomDateRangePopup:public QWidget
15
 class CustomDateRangePopup:public QWidget
31
 QPushButton*applyButton;
31
 QPushButton*applyButton;
32
 };
32
 };
33
 
33
 
34
-/*:657*/
34
+/*:667*/
35
 #line 39 "./daterangeselector.w"
35
 #line 39 "./daterangeselector.w"
36
 
36
 
37
 
37
 
62
 
62
 
63
 #endif
63
 #endif
64
 
64
 
65
-/*:655*/
65
+/*:665*/

+ 1
- 1
src/daterangeselector.w View File

81
 @<CustomDateRangePopup implementation@>
81
 @<CustomDateRangePopup implementation@>
82
 @<DateRangeSelector implementation@>
82
 @<DateRangeSelector implementation@>
83
 
83
 
84
-#if 0
84
+#ifdef __unix__
85
 #include "moc_daterangeselector.cpp"
85
 #include "moc_daterangeselector.cpp"
86
 #endif
86
 #endif
87
 
87
 

+ 2
- 2
src/draglabel.cpp View File

1
-/*992:*/
1
+/*1002:*/
2
 #line 33 "./scales.w"
2
 #line 33 "./scales.w"
3
 
3
 
4
 #include "draglabel.h"
4
 #include "draglabel.h"
26
 }
26
 }
27
 }
27
 }
28
 
28
 
29
-/*:992*/
29
+/*:1002*/

+ 2
- 2
src/draglabel.h View File

1
-/*991:*/
1
+/*1001:*/
2
 #line 13 "./scales.w"
2
 #line 13 "./scales.w"
3
 
3
 
4
 #ifndef TypicaDragLabelInclude
4
 #ifndef TypicaDragLabelInclude
17
 
17
 
18
 #endif
18
 #endif
19
 
19
 
20
-/*:991*/
20
+/*:1001*/

+ 7
- 7
src/helpmenu.cpp View File

1
-/*194:*/
1
+/*204:*/
2
 #line 36 "./helpmenu.w"
2
 #line 36 "./helpmenu.w"
3
 
3
 
4
 #include "helpmenu.h"
4
 #include "helpmenu.h"
5
 #include "abouttypica.h"
5
 #include "abouttypica.h"
6
 #include "licensewindow.h"
6
 #include "licensewindow.h"
7
 
7
 
8
-/*195:*/
8
+/*205:*/
9
 #line 46 "./helpmenu.w"
9
 #line 46 "./helpmenu.w"
10
 
10
 
11
 HelpMenu::HelpMenu():QMenu()
11
 HelpMenu::HelpMenu():QMenu()
24
 #endif
24
 #endif
25
 }
25
 }
26
 
26
 
27
-/*:195*//*196:*/
27
+/*:205*//*206:*/
28
 #line 66 "./helpmenu.w"
28
 #line 66 "./helpmenu.w"
29
 
29
 
30
 void HelpMenu::displayAboutTypica()
30
 void HelpMenu::displayAboutTypica()
33
 aboutBox->show();
33
 aboutBox->show();
34
 }
34
 }
35
 
35
 
36
-/*:196*//*197:*/
36
+/*:206*//*207:*/
37
 #line 76 "./helpmenu.w"
37
 #line 76 "./helpmenu.w"
38
 
38
 
39
 void HelpMenu::displayLicenseWindow()
39
 void HelpMenu::displayLicenseWindow()
42
 window->show();
42
 window->show();
43
 }
43
 }
44
 
44
 
45
-#line 4615 "./typica.w"
45
+#line 4772 "./typica.w"
46
 
46
 
47
 #line 1 "./licensewindow.w"
47
 #line 1 "./licensewindow.w"
48
-/*:197*/
48
+/*:207*/
49
 #line 41 "./helpmenu.w"
49
 #line 41 "./helpmenu.w"
50
 
50
 
51
 
51
 
52
-/*:194*/
52
+/*:204*/

+ 2
- 2
src/helpmenu.h View File

1
-/*193:*/
1
+/*203:*/
2
 #line 16 "./helpmenu.w"
2
 #line 16 "./helpmenu.w"
3
 
3
 
4
 #include <QMenu> 
4
 #include <QMenu> 
18
 
18
 
19
 #endif
19
 #endif
20
 
20
 
21
-/*:193*/
21
+/*:203*/

+ 69
- 0
src/licensewindow.cpp View File

1
+/*209:*/
2
+#line 36 "./licensewindow.w"
3
+
4
+/*213:*/
5
+#line 97 "./licensewindow.w"
6
+
7
+#include "licensewindow.h"
8
+
9
+#include <QSplitter> 
10
+#include <QListWidget> 
11
+#include <QVariant> 
12
+#include <QUrl> 
13
+
14
+#line 4774 "./typica.w"
15
+
16
+/*:213*/
17
+#line 37 "./licensewindow.w"
18
+
19
+/*210:*/
20
+#line 43 "./licensewindow.w"
21
+
22
+LicenseWindow::LicenseWindow()
23
+:QMainWindow(NULL),view(new QWebView)
24
+{
25
+QSplitter*split= new QSplitter;
26
+QListWidget*projects= new QListWidget;
27
+
28
+/*212:*/
29
+#line 79 "./licensewindow.w"
30
+
31
+QListWidgetItem*item= new QListWidgetItem("Typica",projects);
32
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/typica.html")));
33
+projects->setCurrentItem(item);
34
+setWebView(item,NULL);
35
+item= new QListWidgetItem("d3.js",projects);
36
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/d3.html")));
37
+item= new QListWidgetItem("Entypo",projects);
38
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/entypo.html")));
39
+item= new QListWidgetItem("Tango Desktop Project",projects);
40
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/tango.html")));
41
+item= new QListWidgetItem("QextSerialPort",projects);
42
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/qextserialport.html")));
43
+item= new QListWidgetItem("Qt",projects);
44
+item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/qt.html")));
45
+
46
+/*:212*/
47
+#line 50 "./licensewindow.w"
48
+
49
+connect(projects,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
50
+this,SLOT(setWebView(QListWidgetItem*,QListWidgetItem*)));
51
+
52
+split->addWidget(projects);
53
+split->addWidget(view);
54
+setCentralWidget(split);
55
+}
56
+
57
+/*:210*//*211:*/
58
+#line 64 "./licensewindow.w"
59
+
60
+void LicenseWindow::setWebView(QListWidgetItem*current,QListWidgetItem*)
61
+{
62
+view->load(current->data(Qt::UserRole).toUrl());
63
+}
64
+
65
+/*:211*/
66
+#line 38 "./licensewindow.w"
67
+
68
+
69
+/*:209*/

+ 24
- 0
src/licensewindow.h View File

1
+/*208:*/
2
+#line 13 "./licensewindow.w"
3
+
4
+#include <QMainWindow> 
5
+#include <QListWidgetItem> 
6
+#include <QWebView> 
7
+
8
+#ifndef TypicaLicenseHeader
9
+#define TypicaLicenseHeader
10
+
11
+class LicenseWindow:public QMainWindow
12
+{
13
+Q_OBJECT
14
+public:
15
+LicenseWindow();
16
+private slots:
17
+void setWebView(QListWidgetItem*current,QListWidgetItem*);
18
+private:
19
+QWebView*view;
20
+};
21
+
22
+#endif
23
+
24
+/*:208*/

+ 6
- 3
src/moc_helpmenu.cpp View File

22
        6,       // revision
22
        6,       // revision
23
        0,       // classname
23
        0,       // classname
24
        0,    0, // classinfo
24
        0,    0, // classinfo
25
-       1,   14, // methods
25
+       2,   14, // methods
26
        0,    0, // properties
26
        0,    0, // properties
27
        0,    0, // enums/sets
27
        0,    0, // enums/sets
28
        0,    0, // constructors
28
        0,    0, // constructors
31
 
31
 
32
  // slots: signature, parameters, type, tag, flags
32
  // slots: signature, parameters, type, tag, flags
33
       10,    9,    9,    9, 0x0a,
33
       10,    9,    9,    9, 0x0a,
34
+      31,    9,    9,    9, 0x0a,
34
 
35
 
35
        0        // eod
36
        0        // eod
36
 };
37
 };
37
 
38
 
38
 static const char qt_meta_stringdata_HelpMenu[] = {
39
 static const char qt_meta_stringdata_HelpMenu[] = {
39
     "HelpMenu\0\0displayAboutTypica()\0"
40
     "HelpMenu\0\0displayAboutTypica()\0"
41
+    "displayLicenseWindow()\0"
40
 };
42
 };
41
 
43
 
42
 void HelpMenu::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
44
 void HelpMenu::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
46
         HelpMenu *_t = static_cast<HelpMenu *>(_o);
48
         HelpMenu *_t = static_cast<HelpMenu *>(_o);
47
         switch (_id) {
49
         switch (_id) {
48
         case 0: _t->displayAboutTypica(); break;
50
         case 0: _t->displayAboutTypica(); break;
51
+        case 1: _t->displayLicenseWindow(); break;
49
         default: ;
52
         default: ;
50
         }
53
         }
51
     }
54
     }
84
     if (_id < 0)
87
     if (_id < 0)
85
         return _id;
88
         return _id;
86
     if (_c == QMetaObject::InvokeMetaMethod) {
89
     if (_c == QMetaObject::InvokeMetaMethod) {
87
-        if (_id < 1)
90
+        if (_id < 2)
88
             qt_static_metacall(this, _c, _id, _a);
91
             qt_static_metacall(this, _c, _id, _a);
89
-        _id -= 1;
92
+        _id -= 2;
90
     }
93
     }
91
     return _id;
94
     return _id;
92
 }
95
 }

+ 15
- 6
src/moc_typica.cpp View File

3759
        6,       // revision
3759
        6,       // revision
3760
        0,       // classname
3760
        0,       // classname
3761
        0,    0, // classinfo
3761
        0,    0, // classinfo
3762
-       1,   14, // methods
3762
+       4,   14, // methods
3763
        0,    0, // properties
3763
        0,    0, // properties
3764
        0,    0, // enums/sets
3764
        0,    0, // enums/sets
3765
-       1,   19, // constructors
3765
+       1,   34, // constructors
3766
        0,       // flags
3766
        0,       // flags
3767
        0,       // signalCount
3767
        0,       // signalCount
3768
 
3768
 
3769
  // slots: signature, parameters, type, tag, flags
3769
  // slots: signature, parameters, type, tag, flags
3770
       22,   19,   18,   18, 0x08,
3770
       22,   19,   18,   18, 0x08,
3771
+      49,   43,   18,   18, 0x08,
3772
+      74,   43,   18,   18, 0x08,
3773
+      98,   43,   18,   18, 0x08,
3771
 
3774
 
3772
  // constructors: signature, parameters, type, tag, flags
3775
  // constructors: signature, parameters, type, tag, flags
3773
-      55,   43,   18,   18, 0x0e,
3776
+     138,  126,   18,   18, 0x0e,
3774
 
3777
 
3775
        0        // eod
3778
        0        // eod
3776
 };
3779
 };
3777
 
3780
 
3778
 static const char qt_meta_stringdata_RoasterConfWidget[] = {
3781
 static const char qt_meta_stringdata_RoasterConfWidget[] = {
3779
     "RoasterConfWidget\0\0id\0updateRoasterId(int)\0"
3782
     "RoasterConfWidget\0\0id\0updateRoasterId(int)\0"
3780
-    "model,index\0RoasterConfWidget(DeviceTreeModel*,QModelIndex)\0"
3783
+    "value\0updateCapacityCheck(int)\0"
3784
+    "updateCapacity(QString)\0"
3785
+    "updateCapacityUnit(QString)\0model,index\0"
3786
+    "RoasterConfWidget(DeviceTreeModel*,QModelIndex)\0"
3781
 };
3787
 };
3782
 
3788
 
3783
 void RoasterConfWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
3789
 void RoasterConfWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
3792
         RoasterConfWidget *_t = static_cast<RoasterConfWidget *>(_o);
3798
         RoasterConfWidget *_t = static_cast<RoasterConfWidget *>(_o);
3793
         switch (_id) {
3799
         switch (_id) {
3794
         case 0: _t->updateRoasterId((*reinterpret_cast< int(*)>(_a[1]))); break;
3800
         case 0: _t->updateRoasterId((*reinterpret_cast< int(*)>(_a[1]))); break;
3801
+        case 1: _t->updateCapacityCheck((*reinterpret_cast< int(*)>(_a[1]))); break;
3802
+        case 2: _t->updateCapacity((*reinterpret_cast< const QString(*)>(_a[1]))); break;
3803
+        case 3: _t->updateCapacityUnit((*reinterpret_cast< const QString(*)>(_a[1]))); break;
3795
         default: ;
3804
         default: ;
3796
         }
3805
         }
3797
     }
3806
     }
3829
     if (_id < 0)
3838
     if (_id < 0)
3830
         return _id;
3839
         return _id;
3831
     if (_c == QMetaObject::InvokeMetaMethod) {
3840
     if (_c == QMetaObject::InvokeMetaMethod) {
3832
-        if (_id < 1)
3841
+        if (_id < 4)
3833
             qt_static_metacall(this, _c, _id, _a);
3842
             qt_static_metacall(this, _c, _id, _a);
3834
-        _id -= 1;
3843
+        _id -= 4;
3835
     }
3844
     }
3836
     return _id;
3845
     return _id;
3837
 }
3846
 }

+ 31810
- 31572
src/qrc_resources.cpp
File diff suppressed because it is too large
View File


+ 6
- 6
src/resources/Info.plist View File

1
 <?xml version="1.0" encoding="UTF-8"?>
1
 <?xml version="1.0" encoding="UTF-8"?>
2
-<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
3
-<plist version="0.9">
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
 <dict>
4
 <dict>
5
 	<key>CFBundleIconFile</key>
5
 	<key>CFBundleIconFile</key>
6
 	<string>@ICON@</string>
6
 	<string>@ICON@</string>
7
 	<key>CFBundlePackageType</key>
7
 	<key>CFBundlePackageType</key>
8
 	<string>APPL</string>
8
 	<string>APPL</string>
9
 	<key>CFBundleGetInfoString</key>
9
 	<key>CFBundleGetInfoString</key>
10
-	<string>Typica 1.6.4</string>
10
+	<string>Typica 1.7.0</string>
11
 	<key>CFBundleSignature</key>
11
 	<key>CFBundleSignature</key>
12
 	<string>@TYPEINFO@</string>
12
 	<string>@TYPEINFO@</string>
13
 	<key>CFBundleExecutable</key>
13
 	<key>CFBundleExecutable</key>
17
 	<key>CFBundleDisplayName</key>
17
 	<key>CFBundleDisplayName</key>
18
 	<string>Typica</string>
18
 	<string>Typica</string>
19
 	<key>CFBundleShortVersionString</key>
19
 	<key>CFBundleShortVersionString</key>
20
-	<string>1.6.4</string>
20
+	<string>1.7.0</string>
21
 	<key>CFBundleVersion</key>
21
 	<key>CFBundleVersion</key>
22
-	<string>1.6.4</string>
22
+	<string>1.7.0</string>
23
 	<key>NSHumanReadableCopyright</key>
23
 	<key>NSHumanReadableCopyright</key>
24
-	<string>© 2007–2015 Neal Wilson</string>
24
+	<string>© 2007–2016 Neal Wilson</string>
25
 </dict>
25
 </dict>
26
 </plist>
26
 </plist>

+ 7
- 83
src/resources/html/about.html View File

10
 				<div class="topbanner">
10
 				<div class="topbanner">
11
 					<a href="http://www.randomfield.com/programs/typica/"><img src="../icons/appicons/logo96.png" height="96px" width="96px" alt="Typica logo" /></a>
11
 					<a href="http://www.randomfield.com/programs/typica/"><img src="../icons/appicons/logo96.png" height="96px" width="96px" alt="Typica logo" /></a>
12
 					<h1><a href="http://www.randomfield.com/programs/typica/">Typica</a></h1>
12
 					<h1><a href="http://www.randomfield.com/programs/typica/">Typica</a></h1>
13
-					<h2>Version 1.6.4</h2>
13
+					<h2>Version 1.7.0</h2>
14
 				</div>
14
 				</div>
15
 			<div id="maintext">
15
 			<div id="maintext">
16
-				<p>Copyright &copy; 2007&ndash;2015 Neal Evan Wilson
16
+				<p>Copyright &copy; 2007&ndash;2016 Neal Evan Wilson
17
 					<span class="icons">
17
 					<span class="icons">
18
 						<a href="mailto:roaster@wilsonscoffee.com?subject=Thanks%20for%20Typica&amp;body=Message%20initiated%20from%20Typica.">&#9993;</a>
18
 						<a href="mailto:roaster@wilsonscoffee.com?subject=Thanks%20for%20Typica&amp;body=Message%20initiated%20from%20Typica.">&#9993;</a>
19
 						<a href="https://twitter.com/N3Roaster">&#62217;</a>
19
 						<a href="https://twitter.com/N3Roaster">&#62217;</a>
22
 						<a href="http://www.linkedin.com/profile/view?id=179814079">&#62232;</a>
22
 						<a href="http://www.linkedin.com/profile/view?id=179814079">&#62232;</a>
23
 					</span>
23
 					</span>
24
 				</p>
24
 				</p>
25
-				<h3>Special Thanks</h3>
26
-				<p>Ongoing development of Typica is made possible through the
27
-				generous financial contributions from the following:</p>
28
-				
29
-				<p>La Cosecha Coffee Roasters</p>
30
-				<p>Pinnacle Coffee Roasting</p>
31
-				<p>Water Street Coffee Roaster</p>
32
-				<p>Anonymous Funders</p>
33
-				
34
-				<p>Hardware for developing Phidgets 1048 support was provided
35
-				by Phidgets, Inc.</p>
36
-				
37
-				<p>Special thanks also to all who have sent bug reports and
38
-				feature requests.</p>
39
-
40
-				<div class="nbg">
41
-				<div class="nextversion">
42
-				<div class="nextbanner">
43
-					<img src="typica2logo96.png" height="96px" width="96px" alt="Typica 2.0 logo" />
44
-					<h1>Preview of the Next Episode</h3>
45
-				</div>
46
-				<p>This will likely be one of the last releases in the 1.x
47
-				series. Typica 1.0 was released in 2007 and I've learned a
48
-				lot from developing this, using it, and having conversations
49
-				with others about how they're using this. While there is the
50
-				possibility of further improvements, I believe Typica has
51
-				reached a point of maturity with a feature set appropriate for
52
-				many coffee roasting firms.</p>
53
-
54
-				<p>There is a large set of features and extensions that I have
55
-				avoided implementing because the changes would have been too
56
-				intrusive for the code base as it currently exists, but that
57
-				feature set has become too compelling for me to leave undone.
58
-				I am, therefore, attempting to spend as much time as I can on
59
-				the development of Typica 2.0. This removes many of the legacy
60
-				considerations and modernizes the tools and techniques used. I
61
-				hope that the result is a better and more broadly useful Typica
62
-				that can be maintained for another several years.</p>
63
-				
64
-				<p>While Typica is free and will continue to be made freely
65
-				available complete with all source code, there are still costs
66
-				associated with ongoing development and keeping the software
67
-				available. Some of the costs include:
68
-					<ul>
69
-						<li>Obtaining and maintaining hardware appropriate for
70
-						building and testing new releases</li>
71
-						<li>Building and maintaining test rigs for various
72
-						types of data acquisition hardware</li>
73
-						<li>Maintaining the ability to test on new operating
74
-						system releases</li>
75
-						<li>Hosting service</li>
76
-					</ul>
77
-				There is also a huge commitment of time in developing, testing,
78
-				and supporting this.</p>
79
-
80
-				<p>Financial support to date does not come close to covering
81
-				these costs, nor has it been enough that I could consider
82
-				hiring people to improve the pace and quality of ongoing
83
-				development. This is fine. I have no intention of halting work
84
-				on the project as my work has benefitted greatly from having
85
-				this software and I expect even greater benefit from the next
86
-				major release. Personally I find the new feature set exciting
87
-				to work on and am willing to cover funding shortfalls when I
88
-				can afford to do so.</p>
89
-				
90
-				<p>That said, if you're finding this software to be useful in
91
-				your business, if you've had a good support experience, or if
92
-				you'd like to help me continue to develop this software, please
93
-				consider providing some financial support to:</p>
94
-				
95
-				<p>Neal Wilson<br />
96
-				c/o Wilson's Coffee &amp; Tea<br />
97
-				3306 Washington Ave.<br />
98
-				Racine, WI 53405<br />
99
-				USA</p>
100
-				
101
-				<p>Another great way to help Typica right now is to reach out
102
-				to your professional colleagues and let them know that you're
103
-				using Typica and what you like about it.</p>
104
-				</div>
105
-				</div>
25
+				<p>German Translation: Mario Champignon
26
+                                    <span class="icons">
27
+                                        <a href="mailto:mario_champignon@hotmail.com">&#9993;</a>
28
+                                    </span>
29
+                                </p>
106
 				
30
 				
107
 				<h3>License Information</h3>
31
 				<h3>License Information</h3>
108
 				<p>Permission is hereby granted, free of charge, to any person
32
 				<p>Permission is hereby granted, free of charge, to any person

+ 6
- 6
src/scale.cpp View File

1
-/*998:*/
1
+/*1008:*/
2
 #line 131 "./scales.w"
2
 #line 131 "./scales.w"
3
 
3
 
4
 #include "scale.h"
4
 #include "scale.h"
10
 connect(this,SIGNAL(readyRead()),this,SLOT(dataAvailable()));
10
 connect(this,SIGNAL(readyRead()),this,SLOT(dataAvailable()));
11
 }
11
 }
12
 
12
 
13
-/*:998*//*999:*/
13
+/*:1008*//*1009:*/
14
 #line 149 "./scales.w"
14
 #line 149 "./scales.w"
15
 
15
 
16
 void SerialScale::dataAvailable()
16
 void SerialScale::dataAvailable()
24
 }
24
 }
25
 else
25
 else
26
 {
26
 {
27
-/*1000:*/
27
+/*1010:*/
28
 #line 189 "./scales.w"
28
 #line 189 "./scales.w"
29
 
29
 
30
 QStringList responseParts= QString(responseBuffer.simplified()).split(' ');
30
 QStringList responseParts= QString(responseBuffer.simplified()).split(' ');
53
 }
53
 }
54
 emit newMeasurement(weight,unit);
54
 emit newMeasurement(weight,unit);
55
 
55
 
56
-/*:1000*/
56
+/*:1010*/
57
 #line 161 "./scales.w"
57
 #line 161 "./scales.w"
58
 
58
 
59
 responseBuffer.clear();
59
 responseBuffer.clear();
61
 }
61
 }
62
 }
62
 }
63
 
63
 
64
-/*:999*//*1001:*/
64
+/*:1009*//*1011:*/
65
 #line 220 "./scales.w"
65
 #line 220 "./scales.w"
66
 
66
 
67
 void SerialScale::tare()
67
 void SerialScale::tare()
74
 write("!KP\x0D");
74
 write("!KP\x0D");
75
 }
75
 }
76
 
76
 
77
-/*:1001*/
77
+/*:1011*/

+ 2
- 2
src/scale.h View File

1
-/*997:*/
1
+/*1007:*/
2
 #line 103 "./scales.w"
2
 #line 103 "./scales.w"
3
 
3
 
4
 #ifndef TypicaScaleInclude
4
 #ifndef TypicaScaleInclude
25
 
25
 
26
 #endif
26
 #endif
27
 
27
 
28
-/*:997*/
28
+/*:1007*/

+ 2697
- 2453
src/typica.cpp
File diff suppressed because it is too large
View File


+ 6
- 6
src/typica.rc View File

1
 #include <winver.h>
1
 #include <winver.h>
2
 VS_VERSION_INFO	VERSIONINFO
2
 VS_VERSION_INFO	VERSIONINFO
3
-FILEVERSION 	1,6,4,0
4
-PRODUCTVERSION 	1,6,4,0
3
+FILEVERSION 	1,7,0,0
4
+PRODUCTVERSION 	1,7,0,0
5
 FILEFLAGSMASK	0x3fL
5
 FILEFLAGSMASK	0x3fL
6
 #ifdef _DEBUG
6
 #ifdef _DEBUG
7
 	FILEFLAGS	VS_FF_DEBUG
7
 	FILEFLAGS	VS_FF_DEBUG
16
 		BLOCK "040904b0"
16
 		BLOCK "040904b0"
17
 		BEGIN
17
 		BEGIN
18
 			VALUE "CompanyName", "Wilson's Coffee & Tea\0"
18
 			VALUE "CompanyName", "Wilson's Coffee & Tea\0"
19
-			VALUE "FileDescription", "Typica 1.6.4\0"
20
-			VALUE "FileVersion", "1.6.4\0"
19
+			VALUE "FileDescription", "Typica 1.7.0\0"
20
+			VALUE "FileVersion", "1.7.0\0"
21
 			VALUE "InternalName", "Typica\0"
21
 			VALUE "InternalName", "Typica\0"
22
-			VALUE "LegalCopyright", "Copyright 2007-2015 Neal Evan Wilson\0"
22
+			VALUE "LegalCopyright", "Copyright 2007-2016 Neal Evan Wilson\0"
23
 			VALUE "OriginalFilename", "Typica.exe\0"
23
 			VALUE "OriginalFilename", "Typica.exe\0"
24
 			VALUE "ProductName", "Typica\0"
24
 			VALUE "ProductName", "Typica\0"
25
-			VALUE "ProductVersion", "1.6.4\0"
25
+			VALUE "ProductVersion", "1.7.0\0"
26
 		END
26
 		END
27
 	END
27
 	END
28
 	BLOCK "VarFileInfo"
28
 	BLOCK "VarFileInfo"

+ 274
- 25
src/typica.w View File

22
 \mark{\noexpand\nullsec0{A Note on Notation}}
22
 \mark{\noexpand\nullsec0{A Note on Notation}}
23
 \def\pn{Typica}
23
 \def\pn{Typica}
24
 \def\filebase{typica}
24
 \def\filebase{typica}
25
-\def\version{1.6.4 \number\year-\number\month-\number\day}
26
-\def\years{2007--2015}
25
+\def\version{1.7.0 \number\year-\number\month-\number\day}
26
+\def\years{2007--2016}
27
 \def\title{\pn{} (Version \version)}
27
 \def\title{\pn{} (Version \version)}
28
 \newskip\dangerskipb
28
 \newskip\dangerskipb
29
 \newskip\dangerskip
29
 \newskip\dangerskip
623
 #include <QtDebug>
623
 #include <QtDebug>
624
 #include <QtXmlPatterns>
624
 #include <QtXmlPatterns>
625
 #include <QtWebKit>
625
 #include <QtWebKit>
626
+#include <QtSvg>
626
 
627
 
627
 @ New code is being written in separate files in a long term effort to improve
628
 @ New code is being written in separate files in a long term effort to improve
628
 organization of the code. The result of this is that some additional headers
629
 organization of the code. The result of this is that some additional headers
921
     return QScriptValue();
922
     return QScriptValue();
922
 }
923
 }
923
 
924
 
925
+@* Scripting QMessageBox.
926
+
927
+\noindent Some features require that \pn{} pauses an operation until further
928
+information can be obtained. An example of this is discretionary validation
929
+where input is checked and if it seems unlikely but not impossible to be
930
+correct a dialog should come up asking if that input is correct. If it is not,
931
+the operation should be cancelled and the person using \pn{} should be allowed
932
+to correct the information and try again.
933
+
934
+For this use case, it is not necessary to fully expose the |QMessageBox| class.
935
+Instead, it is enough to provide a function that will raise an appropriate
936
+message and return the selected action.
937
+
938
+@<Function prototypes for scripting@>=
939
+QScriptValue displayWarning(QScriptContext *context, QScriptEngine *engine);
940
+QScriptValue displayError(QScriptContext *context, QScriptEngine *engine);
941
+
942
+@ This function is exposed to the host environment.
943
+
944
+@<Set up the scripting engine@>=
945
+constructor = engine->newFunction(displayWarning);
946
+engine->globalObject().setProperty("displayWarning", constructor);
947
+constructor = engine->newFunction(displayError);
948
+engine->globalObject().setProperty("displayError", constructor);
949
+
950
+@ The function takes some arguments.
951
+
952
+@<Functions for scripting@>=
953
+QScriptValue displayWarning(QScriptContext *context, QScriptEngine *)
954
+{
955
+    QMessageBox::StandardButton selection = QMessageBox::warning(NULL,
956
+        argument<QString>(0, context),
957
+        argument<QString>(1, context),
958
+        QMessageBox::Ok | QMessageBox::Cancel);
959
+    if(selection == QMessageBox::Ok) {
960
+        return QScriptValue(true);
961
+    }
962
+    return QScriptValue(false);
963
+}
964
+
965
+QScriptValue displayError(QScriptContext *context, QScriptEngine *)
966
+{
967
+    QMessageBox::critical(NULL, argument<QString>(0, context),
968
+                          argument<QString>(1, context));
969
+    return QScriptValue();
970
+}
971
+
924
 @* Scripting QMainWindow.
972
 @* Scripting QMainWindow.
925
 
973
 
926
 \noindent Rather than directly exposing |QMainWindow| to the scripting engine,
974
 \noindent Rather than directly exposing |QMainWindow| to the scripting engine,
1363
     setQFrameProperties(value, engine);
1411
     setQFrameProperties(value, engine);
1364
 }
1412
 }
1365
 
1413
 
1414
+@* Scripting QSvgWidget.
1415
+
1416
+\noindent Sometimes it is useful to provide a space for simple drawings without
1417
+the need for all of the other capabilities of a web view. This was introduced
1418
+as a way to draw box plots to help guide the creation of roast specifications.
1419
+
1420
+@<Function prototypes for scripting@>=
1421
+void setQSvgWidgetProperties(QScriptValue value, QScriptEngine *engine);
1422
+QScriptValue constructQSvgWidget(QScriptContext *context,
1423
+                                 QScriptEngine *engine);
1424
+QScriptValue QSvgWidget_loadDevice(QScriptContext *context,
1425
+                                   QScriptEngine *engine);
1426
+void addSvgWidgetToLayout(QDomElement element, QStack<QWidget *> *widgetStack,
1427
+                          QStack<QLayout *> *layoutStack);
1428
+
1429
+@ The constructor must be passed to the scripting engine.
1430
+
1431
+@<Set up the scripting engine@>=
1432
+constructor = engine->newFunction(constructQSvgWidget);
1433
+value = engine->newQMetaObject(&QSvgWidget::staticMetaObject, constructor);
1434
+engine->globalObject().setProperty("QSvgWidget", value);
1435
+
1436
+@ The constructor is trivial.
1437
+
1438
+@<Functions for scripting@>=
1439
+QScriptValue constructQSvgWidget(QScriptContext *,
1440
+                                 QScriptEngine *engine)
1441
+{
1442
+    QScriptValue object = engine->newQObject(new QSvgWidget);
1443
+    setQSvgWidgetProperties(object, engine);
1444
+    return object;
1445
+}
1446
+
1447
+@ A property is added that allows loading data from a |QIODevice|.
1448
+
1449
+@<Functions for scripting@>=
1450
+void setQSvgWidgetProperties(QScriptValue value, QScriptEngine *engine)
1451
+{
1452
+    setQWidgetProperties(value, engine);
1453
+    value.setProperty("loadDevice",
1454
+                      engine->newFunction(QSvgWidget_loadDevice));
1455
+}
1456
+
1457
+QScriptValue QSvgWidget_loadDevice(QScriptContext *context, QScriptEngine *)
1458
+{
1459
+    if(context->argumentCount() == 1)
1460
+    {
1461
+        QSvgWidget *self = getself<@[QSvgWidget *@]>(context);
1462
+        QIODevice *device = argument<QIODevice *>(0, context);
1463
+        device->reset();
1464
+        QByteArray data = device->readAll();
1465
+        self->load(data);
1466
+    }
1467
+    else
1468
+    {
1469
+        context->throwError("Incorrect number of arguments passed to "@|
1470
+                            "QSvgWidget::loadData(). This method takes one "@|
1471
+                            "QIODevice as an argument.");
1472
+    }
1473
+    return QScriptValue();
1474
+}
1475
+
1476
+@ Additional work is needed to allow including this from the XML description of
1477
+a window.
1478
+
1479
+@<Additional box layout elements@>=
1480
+else if(currentElement.tagName() == "svgwidget")
1481
+{
1482
+    addSvgWidgetToLayout(currentElement, widgetStack, layoutStack);
1483
+}
1484
+
1485
+@ The function used to create this follows the usual pattern.
1486
+
1487
+@<Functions for scripting@>=
1488
+void addSvgWidgetToLayout(QDomElement element, QStack<QWidget *> *,
1489
+                          QStack<QLayout *> *layoutStack)
1490
+{
1491
+    QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
1492
+    QSvgWidget *widget = new QSvgWidget;
1493
+    layout->addWidget(widget);
1494
+    QString id = element.attribute("id");
1495
+    if(!id.isEmpty())
1496
+    {
1497
+        widget->setObjectName(id);
1498
+    }
1499
+}
1500
+
1366
 @* Scripting QLineEdit.
1501
 @* Scripting QLineEdit.
1367
 
1502
 
1368
 \noindent Similarly, we may want to allow line edits in interfaces defined
1503
 \noindent Similarly, we may want to allow line edits in interfaces defined
4014
                                   QScriptEngine *engine);
4149
                                   QScriptEngine *engine);
4015
 QScriptValue setTabOrder(QScriptContext *context, QScriptEngine *engine);
4150
 QScriptValue setTabOrder(QScriptContext *context, QScriptEngine *engine);
4016
 QScriptValue saveFileFromDatabase(QScriptContext *context, QScriptEngine *engine);
4151
 QScriptValue saveFileFromDatabase(QScriptContext *context, QScriptEngine *engine);
4152
+QScriptValue scriptTr(QScriptContext *context, QScriptEngine *engine);
4017
 
4153
 
4018
 @ These functions are passed to the scripting engine.
4154
 @ These functions are passed to the scripting engine.
4019
 
4155
 
4029
                                    engine->newFunction(setTabOrder));
4165
                                    engine->newFunction(setTabOrder));
4030
 engine->globalObject().setProperty("saveFileFromDatabase",
4166
 engine->globalObject().setProperty("saveFileFromDatabase",
4031
                                    engine->newFunction(saveFileFromDatabase));
4167
                                    engine->newFunction(saveFileFromDatabase));
4168
+engine->globalObject().setProperty("TTR", engine->newFunction(scriptTr));
4032
 
4169
 
4033
 @ These functions are not part of an object. They expect a string specifying
4170
 @ These functions are not part of an object. They expect a string specifying
4034
 the path to a file and return a string with either the name of the file without
4171
 the path to a file and return a string with either the name of the file without
4155
     return QScriptValue();
4292
     return QScriptValue();
4156
 }
4293
 }
4157
 
4294
 
4295
+@ This function is used to allow text that must be placed in scripts to be
4296
+translated into other languages.
4297
+
4298
+@<Functions for scripting@>=
4299
+QScriptValue scriptTr(QScriptContext *context, QScriptEngine *)
4300
+{
4301
+    return QScriptValue(QCoreApplication::translate(
4302
+        "configuration",
4303
+        argument<QString>(1, context).toUtf8().data()));
4304
+}
4158
 
4305
 
4159
 @** Application Configuration.
4306
 @** Application Configuration.
4160
 
4307
 
4204
     QFile file(filename);
4351
     QFile file(filename);
4205
     QFileInfo info(filename);
4352
     QFileInfo info(filename);
4206
     directory = info.dir();
4353
     directory = info.dir();
4354
+    QTextCodec::setCodecForTr(QTextCodec::codecForName("utf-8"));
4355
+    QTranslator *configtr = new QTranslator;
4356
+    if(configtr->load(QString("config.%1").arg(QLocale::system().name()),
4357
+                     QString("%1/Translations").arg(directory.canonicalPath())))
4358
+    {
4359
+        QCoreApplication::installTranslator(configtr);
4360
+    }
4207
     settings.setValue("config", directory.path());
4361
     settings.setValue("config", directory.path());
4208
     if(file.open(QIODevice::ReadOnly))
4362
     if(file.open(QIODevice::ReadOnly))
4209
     {
4363
     {
4528
         }
4682
         }
4529
         else if(element.tagName() == "layout")
4683
         else if(element.tagName() == "layout")
4530
         {
4684
         {
4685
+            element.setAttribute("trcontext", "configuration");
4531
             addLayoutToWidget(element, &widgetStack, &layoutStack);
4686
             addLayoutToWidget(element, &widgetStack, &layoutStack);
4532
         }
4687
         }
4533
         else if(element.tagName() == "menu")
4688
         else if(element.tagName() == "menu")
4560
 bar->setObjectName("menuBar");
4715
 bar->setObjectName("menuBar");
4561
 if(element.hasAttribute("name"))
4716
 if(element.hasAttribute("name"))
4562
 {
4717
 {
4563
-    QMenu *menu = bar->addMenu(element.attribute("name"));
4718
+    QMenu *menu = bar->addMenu(QCoreApplication::translate("configuration",
4719
+                                                           element.attribute("name").toUtf8().data()));
4564
     menu->setParent(bar);
4720
     menu->setParent(bar);
4565
     if(element.hasAttribute("type"))
4721
     if(element.hasAttribute("type"))
4566
     {
4722
     {
4592
         QDomElement itemElement = item.toElement();
4748
         QDomElement itemElement = item.toElement();
4593
         if(itemElement.tagName() == "item")
4749
         if(itemElement.tagName() == "item")
4594
         {
4750
         {
4595
-            QAction *itemAction = new QAction(itemElement.text(), menu);
4751
+            QAction *itemAction = new QAction(QCoreApplication::translate("configuration",
4752
+                                              itemElement.text().toUtf8().data()), menu);
4596
             if(itemElement.hasAttribute("id"))
4753
             if(itemElement.hasAttribute("id"))
4597
             {
4754
             {
4598
                 itemAction->setObjectName(itemElement.attribute("id"));
4755
                 itemAction->setObjectName(itemElement.attribute("id"));
4712
                 QWidget *widget = new QWidget;
4869
                 QWidget *widget = new QWidget;
4713
                 layout->addWidget(widget);
4870
                 layout->addWidget(widget);
4714
                 widgetStack->push(widget);
4871
                 widgetStack->push(widget);
4872
+                currentElement.setAttribute("trcontext", "configuration");
4715
                 populateWidget(currentElement, widgetStack, layoutStack);
4873
                 populateWidget(currentElement, widgetStack, layoutStack);
4716
                 widgetStack->pop();
4874
                 widgetStack->pop();
4717
             }
4875
             }
4822
             QHBoxLayout *cell = new QHBoxLayout;
4980
             QHBoxLayout *cell = new QHBoxLayout;
4823
             layout->addLayout(cell, row, column, vspan, hspan);
4981
             layout->addLayout(cell, row, column, vspan, hspan);
4824
             layoutStack->push(cell);
4982
             layoutStack->push(cell);
4983
+            columnElement.setAttribute("trcontext", "configuration");
4825
             populateBoxLayout(columnElement, widgetStack, layoutStack);
4984
             populateBoxLayout(columnElement, widgetStack, layoutStack);
4826
             layoutStack->pop();
4985
             layoutStack->pop();
4827
         }
4986
         }
4844
         if(current.isElement())
5003
         if(current.isElement())
4845
         {
5004
         {
4846
             currentElement = current.toElement();
5005
             currentElement = current.toElement();
5006
+            currentElement.setAttribute("trcontext", "configuration");
4847
             if(currentElement.tagName() == "button")
5007
             if(currentElement.tagName() == "button")
4848
             {
5008
             {
4849
                 addButtonToLayout(currentElement, widgetStack, layoutStack);
5009
                 addButtonToLayout(currentElement, widgetStack, layoutStack);
4869
             {
5029
             {
4870
                 QBoxLayout *layout =
5030
                 QBoxLayout *layout =
4871
                     qobject_cast<QBoxLayout *>(layoutStack->top());
5031
                     qobject_cast<QBoxLayout *>(layoutStack->top());
4872
-                QLabel *label = new QLabel(currentElement.text());
5032
+                QLabel *label = new QLabel(QCoreApplication::translate(
5033
+                    "configuration",
5034
+                    currentElement.text().toUtf8().data()));
4873
                 layout->addWidget(label);
5035
                 layout->addWidget(label);
4874
             }
5036
             }
4875
             else if(currentElement.tagName() == "lcdtemperature")
5037
             else if(currentElement.tagName() == "lcdtemperature")
5013
         if(current.isElement())
5175
         if(current.isElement())
5014
         {
5176
         {
5015
             currentElement = current.toElement();
5177
             currentElement = current.toElement();
5178
+            currentElement.setAttribute("trcontext", "configuration");
5016
             if(currentElement.tagName() == "decoration")
5179
             if(currentElement.tagName() == "decoration")
5017
             {
5180
             {
5018
                 addDecorationToSplitter(currentElement, widgetStack,
5181
                 addDecorationToSplitter(currentElement, widgetStack,
5158
 labeled.
5321
 labeled.
5159
 
5322
 
5160
 @<Set up decoration@>=
5323
 @<Set up decoration@>=
5161
-QString labelText = element.attribute("name");
5324
+QString labelText =
5325
+    QCoreApplication::translate("configuration",
5326
+    element.attribute("name").toUtf8().data());
5162
 Qt::Orientations@, orientation = Qt::Horizontal;
5327
 Qt::Orientations@, orientation = Qt::Horizontal;
5163
 if(element.hasAttribute("type"))
5328
 if(element.hasAttribute("type"))
5164
 {
5329
 {
5254
             currentElement = current.toElement();
5419
             currentElement = current.toElement();
5255
             if(currentElement.tagName() == "layout")
5420
             if(currentElement.tagName() == "layout")
5256
             {
5421
             {
5422
+                currentElement.setAttribute("trcontext", "configuration");
5257
                 addLayoutToWidget(currentElement, widgetStack, layoutStack);
5423
                 addLayoutToWidget(currentElement, widgetStack, layoutStack);
5258
             }
5424
             }
5259
         }
5425
         }
5269
                        QStack<QLayout *> *layoutStack)
5435
                        QStack<QLayout *> *layoutStack)
5270
 {
5436
 {
5271
     QAbstractButton *button = NULL;
5437
     QAbstractButton *button = NULL;
5272
-    QString text = element.attribute("name");
5438
+    QString text =
5439
+        QCoreApplication::translate("configuration",
5440
+                                    element.attribute("name").toUtf8().data());
5273
     if(element.hasAttribute("type"))
5441
     if(element.hasAttribute("type"))
5274
     {
5442
     {
5275
         QString type = element.attribute("type");
5443
         QString type = element.attribute("type");
5323
     AnnotationSpinBox *box = new AnnotationSpinBox("", "", NULL);
5491
     AnnotationSpinBox *box = new AnnotationSpinBox("", "", NULL);
5324
     if(element.hasAttribute("pretext"))
5492
     if(element.hasAttribute("pretext"))
5325
     {
5493
     {
5326
-        box->setPretext(element.attribute("pretext"));
5494
+        box->setPretext(QCoreApplication::translate(
5495
+                        "configuration",
5496
+                        element.attribute("pretext").toUtf8().data()));
5327
     }
5497
     }
5328
     if(element.hasAttribute("posttext"))
5498
     if(element.hasAttribute("posttext"))
5329
     {
5499
     {
5330
-        box->setPosttext(element.attribute("posttext"));
5500
+        box->setPosttext(QCoreApplication::translate(
5501
+                         "configuration",
5502
+                         element.attribute("posttext").toUtf8().data()));
5331
     }
5503
     }
5332
     if(element.hasAttribute("series"))
5504
     if(element.hasAttribute("series"))
5333
     {
5505
     {
5377
                           QStack<QLayout *> *)
5549
                           QStack<QLayout *> *)
5378
 {
5550
 {
5379
     ZoomLog *widget = new ZoomLog;
5551
     ZoomLog *widget = new ZoomLog;
5380
-    if(!widget)
5381
-    {
5382
-        qDebug() << "Error constructing widget!";
5383
-    }
5384
     if(element.hasAttribute("id"))
5552
     if(element.hasAttribute("id"))
5385
     {
5553
     {
5386
         widget->setObjectName(element.attribute("id"));
5554
         widget->setObjectName(element.attribute("id"));
5399
                 currentElement = current.toElement();
5567
                 currentElement = current.toElement();
5400
                 if(currentElement.tagName() == "column")
5568
                 if(currentElement.tagName() == "column")
5401
                 {
5569
                 {
5402
-                    QString text = currentElement.text();
5570
+                    QString text =
5571
+                        QCoreApplication::translate(
5572
+                            "configuration",
5573
+                            currentElement.text().toUtf8().data());
5403
                     widget->setHeaderData(column, text);
5574
                     widget->setHeaderData(column, text);
5404
                     column++;
5575
                     column++;
5405
                 }
5576
                 }
5543
                 {
5714
                 {
5544
                     if(currentElement.hasAttribute("name"))
5715
                     if(currentElement.hasAttribute("name"))
5545
                     {
5716
                     {
5546
-                        model->setHeaderData(currentColumn, Qt::Horizontal,
5547
-                                             currentElement.attribute("name"));
5717
+                        model->setHeaderData(currentColumn,
5718
+                            Qt::Horizontal,
5719
+                            QCoreApplication::translate(
5720
+                                "configuration",
5721
+                                currentElement.attribute("name").toUtf8().data()));
5548
                     }
5722
                     }
5549
                     if(currentElement.hasAttribute("delegate"))
5723
                     if(currentElement.hasAttribute("delegate"))
5550
                     {
5724
                     {
5925
     value.setProperty("day", engine->newFunction(QDateTimeEdit_day));
6099
     value.setProperty("day", engine->newFunction(QDateTimeEdit_day));
5926
     value.setProperty("month", engine->newFunction(QDateTimeEdit_month));
6100
     value.setProperty("month", engine->newFunction(QDateTimeEdit_month));
5927
     value.setProperty("year", engine->newFunction(QDateTimeEdit_year));
6101
     value.setProperty("year", engine->newFunction(QDateTimeEdit_year));
6102
+    value.setProperty("setToCurrentTime",
6103
+                      engine->newFunction(QDateTimeEdit_setToCurrentTime));
5928
 }
6104
 }
5929
 
6105
 
5930
 @ Certain operations on a |QDateEdit| are easier with a few convenience
6106
 @ Certain operations on a |QDateEdit| are easier with a few convenience
5969
     return QScriptValue(self->date().year());
6145
     return QScriptValue(self->date().year());
5970
 }
6146
 }
5971
 
6147
 
6148
+QScriptValue QDateTimeEdit_setToCurrentTime(QScriptContext *context, QScriptEngine *)
6149
+{
6150
+    QDateTimeEdit *self = getself<QDateTimeEdit *>(context);
6151
+    self->setDateTime(QDateTime::currentDateTime());
6152
+    return QScriptValue();
6153
+}
6154
+
5972
 @ A few function prototypes are needed for this.
6155
 @ A few function prototypes are needed for this.
5973
 
6156
 
5974
 @<Function prototypes for scripting@>=
6157
 @<Function prototypes for scripting@>=
5980
 QScriptValue QDateTimeEdit_month(QScriptContext *context,
6163
 QScriptValue QDateTimeEdit_month(QScriptContext *context,
5981
                                  QScriptEngine *engine);
6164
                                  QScriptEngine *engine);
5982
 QScriptValue QDateTimeEdit_year(QScriptContext *context, QScriptEngine *engine);
6165
 QScriptValue QDateTimeEdit_year(QScriptContext *context, QScriptEngine *engine);
6166
+QScriptValue QDateTimeEdit_setToCurrentTime(QScriptContext *context, QScriptEngine *engine);
5983
 
6167
 
5984
 @ In order to get to objects created from the XML description, it is necessary
6168
 @ In order to get to objects created from the XML description, it is necessary
5985
 to provide a function that can be called to retrieve children of a given widget.
6169
 to provide a function that can be called to retrieve children of a given widget.
6116
 {
6300
 {
6117
     setQLineEditProperties(value, engine);
6301
     setQLineEditProperties(value, engine);
6118
 }
6302
 }
6303
+else if(className == "QSvgWidget")
6304
+{
6305
+    setQSvgWidgetProperties(value, engine);
6306
+}
6119
 
6307
 
6120
 @ In the list of classes, the SaltTable entry is for a class which does not
6308
 @ In the list of classes, the SaltTable entry is for a class which does not
6121
 strictly exist on its own. It is, however, useful to provide some custom
6309
 strictly exist on its own. It is, however, useful to provide some custom
12437
 details, see the Qt Linguist manual.
12625
 details, see the Qt Linguist manual.
12438
 
12626
 
12439
 @<Load translation objects@>=
12627
 @<Load translation objects@>=
12440
-QTranslator base;
12441
-if(base.load(QString("qt_%1").arg(QLocale::system().name())))
12628
+QTranslator *base = new QTranslator;
12629
+if(base->load(QString("qt_%1").arg(QLocale::system().name()), QString("%1/Translations").arg(QCoreApplication::applicationDirPath())))
12442
 {
12630
 {
12443
-    installTranslator(&base);
12631
+    installTranslator(base);
12444
 }
12632
 }
12445
-QTranslator app;
12446
-if(app.load(QString("%1_%2").arg("Typica").arg(QLocale::system().name())))
12633
+QTranslator *app = new QTranslator;
12634
+if(app->load(QString("%1_%2").arg("Typica").arg(QLocale::system().name()), QString("%1/Translations").arg(QCoreApplication::applicationDirPath())))
12447
 {
12635
 {
12448
-    installTranslator(&app);
12636
+    installTranslator(app);
12449
 }
12637
 }
12450
 
12638
 
12451
 @ We also want to be able to access the application instance from within the
12639
 @ We also want to be able to access the application instance from within the
13286
         QFormLayout *formLayout;
13474
         QFormLayout *formLayout;
13287
         QComboBox *driver;
13475
         QComboBox *driver;
13288
         QLineEdit *hostname;
13476
         QLineEdit *hostname;
13477
+        QLineEdit *portnumber;
13289
         QLineEdit *dbname;
13478
         QLineEdit *dbname;
13290
         QLineEdit *user;
13479
         QLineEdit *user;
13291
         QLineEdit *password;
13480
         QLineEdit *password;
13300
 @<SqlConnectionSetup implementation@>=
13489
 @<SqlConnectionSetup implementation@>=
13301
 SqlConnectionSetup::SqlConnectionSetup() :
13490
 SqlConnectionSetup::SqlConnectionSetup() :
13302
     formLayout(new QFormLayout), driver(new QComboBox), hostname(new QLineEdit),
13491
     formLayout(new QFormLayout), driver(new QComboBox), hostname(new QLineEdit),
13492
+    portnumber(new QLineEdit),
13303
     dbname(new QLineEdit), user(new QLineEdit), password(new QLineEdit),
13493
     dbname(new QLineEdit), user(new QLineEdit), password(new QLineEdit),
13304
     layout(new QVBoxLayout), buttons(new QHBoxLayout),
13494
     layout(new QVBoxLayout), buttons(new QHBoxLayout),
13305
     cancelButton(new QPushButton(tr("Cancel"))),
13495
     cancelButton(new QPushButton(tr("Cancel"))),
13308
     driver->addItem("PostgreSQL", "QPSQL");
13498
     driver->addItem("PostgreSQL", "QPSQL");
13309
     formLayout->addRow(tr("Database driver:"), driver);
13499
     formLayout->addRow(tr("Database driver:"), driver);
13310
     formLayout->addRow(tr("Host name:"), hostname);
13500
     formLayout->addRow(tr("Host name:"), hostname);
13501
+    formLayout->addRow(tr("Port number:"), portnumber);
13502
+    portnumber->setText("5432");
13311
     formLayout->addRow(tr("Database name:"), dbname);
13503
     formLayout->addRow(tr("Database name:"), dbname);
13312
     formLayout->addRow(tr("User name:"), user);
13504
     formLayout->addRow(tr("User name:"), user);
13313
     password->setEchoMode(QLineEdit::Password);
13505
     password->setEchoMode(QLineEdit::Password);
13319
     buttons->addWidget(connectButton);
13511
     buttons->addWidget(connectButton);
13320
     layout->addLayout(buttons);
13512
     layout->addLayout(buttons);
13321
     connect(connectButton, SIGNAL(clicked(bool)), this, SLOT(testConnection()));
13513
     connect(connectButton, SIGNAL(clicked(bool)), this, SLOT(testConnection()));
13514
+    connectButton->setDefault(true);
13322
     setLayout(layout);
13515
     setLayout(layout);
13323
     setModal(true);
13516
     setModal(true);
13324
 }
13517
 }
13339
                                   toString());
13532
                                   toString());
13340
     database.setConnectOptions("application_name=Typica");
13533
     database.setConnectOptions("application_name=Typica");
13341
     database.setHostName(hostname->text());
13534
     database.setHostName(hostname->text());
13535
+    database.setPort(portnumber->text().toInt());
13342
     database.setDatabaseName(dbname->text());
13536
     database.setDatabaseName(dbname->text());
13343
     database.setUserName(user->text());
13537
     database.setUserName(user->text());
13344
     database.setPassword(password->text());
13538
     database.setPassword(password->text());
13349
         settings.setValue("database/driver",
13543
         settings.setValue("database/driver",
13350
                           driver->itemData(driver->currentIndex()).toString());
13544
                           driver->itemData(driver->currentIndex()).toString());
13351
         settings.setValue("database/hostname", hostname->text());
13545
         settings.setValue("database/hostname", hostname->text());
13546
+        settings.setValue("database/portnumber", portnumber->text());
13352
         settings.setValue("database/dbname", dbname->text());
13547
         settings.setValue("database/dbname", dbname->text());
13353
         settings.setValue("database/user", user->text());
13548
         settings.setValue("database/user", user->text());
13354
         settings.setValue("database/password", password->text());
13549
         settings.setValue("database/password", password->text());
13381
     QSqlDatabase::addDatabase(settings.value("database/driver").toString());
13576
     QSqlDatabase::addDatabase(settings.value("database/driver").toString());
13382
 database.setConnectOptions("application_name=Typica");
13577
 database.setConnectOptions("application_name=Typica");
13383
 database.setHostName(settings.value("database/hostname").toString());
13578
 database.setHostName(settings.value("database/hostname").toString());
13579
+database.setPort(settings.value("database/portnumber", 5432).toInt());
13384
 database.setDatabaseName(settings.value("database/dbname").toString());
13580
 database.setDatabaseName(settings.value("database/dbname").toString());
13385
 database.setUserName(settings.value("database/user").toString());
13581
 database.setUserName(settings.value("database/user").toString());
13386
 database.setPassword(settings.value("database/password").toString());
13582
 database.setPassword(settings.value("database/password").toString());
13728
     QDomDocument document;
13924
     QDomDocument document;
13729
     document.setContent(&file, true);
13925
     document.setContent(&file, true);
13730
     QDomElement root = document.documentElement();
13926
     QDomElement root = document.documentElement();
13927
+    QString translationContext = root.attribute("id");
13731
     QDomNode titleNode = root.elementsByTagName("reporttitle").at(0);
13928
     QDomNode titleNode = root.elementsByTagName("reporttitle").at(0);
13732
     if(!titleNode.isNull())
13929
     if(!titleNode.isNull())
13733
     {
13930
     {
13734
         QDomElement titleElement = titleNode.toElement();
13931
         QDomElement titleElement = titleNode.toElement();
13735
-        QString title = titleElement.text();
13932
+        QString title = QCoreApplication::translate("configuration",
13933
+                                                    titleElement.text().toUtf8().data());
13736
         if(!title.isEmpty())
13934
         if(!title.isEmpty())
13737
         {
13935
         {
13738
             QStringList hierarchy = title.split(":->");
13936
             QStringList hierarchy = title.split(":->");
15993
                                             const QModelIndex &index);
16191
                                             const QModelIndex &index);
15994
     @[private slots@]:@/
16192
     @[private slots@]:@/
15995
         void updateRoasterId(int id);
16193
         void updateRoasterId(int id);
16194
+        void updateCapacityCheck(int value);
16195
+        void updateCapacity(const QString &value);
16196
+        void updateCapacityUnit(const QString &value);
15996
 };
16197
 };
15997
 
16198
 
15998
 @ Aside from the ID number used to identify the roaster in the database we also
16199
 @ Aside from the ID number used to identify the roaster in the database we also
16074
     idLayout->addWidget(idLabel);
16275
     idLayout->addWidget(idLabel);
16075
     QSpinBox *id = new QSpinBox;
16276
     QSpinBox *id = new QSpinBox;
16076
     idLayout->addWidget(id);
16277
     idLayout->addWidget(id);
16278
+    idLayout->addStretch();
16077
     layout->addLayout(idLayout);
16279
     layout->addLayout(idLayout);
16280
+    QHBoxLayout *capacityLayout = new QHBoxLayout;
16281
+    QCheckBox *capacityCheckEnabled = new QCheckBox(tr("Maximum batch size:"));
16282
+    QDoubleSpinBox *capacity = new QDoubleSpinBox;
16283
+    capacity->setMinimum(0.0);
16284
+    capacity->setDecimals(3);
16285
+    capacity->setMaximum(999999.999);
16286
+    QComboBox *capacityUnit = new QComboBox;
16287
+    capacityUnit->addItem("g");
16288
+    capacityUnit->addItem("Kg");
16289
+    capacityUnit->addItem("oz");
16290
+    capacityUnit->addItem("Lb");
16291
+    capacityUnit->setCurrentIndex(3);
16292
+    capacityLayout->addWidget(capacityCheckEnabled);
16293
+    capacityLayout->addWidget(capacity);
16294
+    capacityLayout->addWidget(capacityUnit);
16295
+    capacityLayout->addStretch();
16296
+    layout->addLayout(capacityLayout);
16297
+    layout->addStretch();
16078
     @<Get device configuration data for current node@>@;
16298
     @<Get device configuration data for current node@>@;
16079
     for(int i = 0; i < configData.size(); i++)
16299
     for(int i = 0; i < configData.size(); i++)
16080
     {
16300
     {
16082
         if(node.attribute("name") == "databaseid")
16302
         if(node.attribute("name") == "databaseid")
16083
         {
16303
         {
16084
             id->setValue(node.attribute("value").toInt());
16304
             id->setValue(node.attribute("value").toInt());
16085
-            break;
16305
+        }
16306
+        else if(node.attribute("name") == "checkcapacity")
16307
+        {
16308
+            capacityCheckEnabled->setChecked(node.attribute("value") == "true");
16309
+        }
16310
+        else if(node.attribute("name") == "capacity")
16311
+        {
16312
+            capacity->setValue(node.attribute("value").toDouble());
16313
+        }
16314
+        else if(node.attribute("name") == "capacityunit")
16315
+        {
16316
+            capacityUnit->setCurrentIndex(capacityUnit->findText(node.attribute("value")));
16086
         }
16317
         }
16087
     }
16318
     }
16088
     updateRoasterId(id->value());
16319
     updateRoasterId(id->value());
16089
     connect(id, SIGNAL(valueChanged(int)), this, SLOT(updateRoasterId(int)));
16320
     connect(id, SIGNAL(valueChanged(int)), this, SLOT(updateRoasterId(int)));
16321
+    connect(capacityCheckEnabled, SIGNAL(stateChanged(int)), this, SLOT(updateCapacityCheck(int)));
16322
+    connect(capacity, SIGNAL(valueChanged(QString)), this, SLOT(updateCapacity(QString)));
16323
+    connect(capacityUnit, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateCapacityUnit(QString)));
16090
     setLayout(layout);
16324
     setLayout(layout);
16091
 }
16325
 }
16092
 
16326
 
16103
 QDomNodeList configData = referenceElement.elementsByTagName("attribute");
16337
 QDomNodeList configData = referenceElement.elementsByTagName("attribute");
16104
 QDomElement node;
16338
 QDomElement node;
16105
 
16339
 
16106
-@ We need to propagate changes to the ID number field to the device
16340
+@ We need to propagate changes to the configuration fields to the device
16107
 configuration document. The |updateAttribute()| method in the base class
16341
 configuration document. The |updateAttribute()| method in the base class
16108
 makes this trivial.
16342
 makes this trivial.
16109
 
16343
 
16113
     updateAttribute("databaseid", QString("%1").arg(id));
16347
     updateAttribute("databaseid", QString("%1").arg(id));
16114
 }
16348
 }
16115
 
16349
 
16350
+void RoasterConfWidget::updateCapacityCheck(int value)
16351
+{
16352
+    updateAttribute("checkcapacity", value == Qt::Checked ? "true" : "false");
16353
+}
16354
+
16355
+void RoasterConfWidget::updateCapacity(const QString &value)
16356
+{
16357
+    updateAttribute("capacity", value);
16358
+}
16359
+
16360
+void RoasterConfWidget::updateCapacityUnit(const QString &value)
16361
+{
16362
+    updateAttribute("capacityunit", value);
16363
+}
16364
+
16116
 @ Finally we must register the configuration widget so that it can be
16365
 @ Finally we must register the configuration widget so that it can be
16117
 instantiated at the appropriate time.
16366
 instantiated at the appropriate time.
16118
 
16367
 

+ 6
- 6
src/units.cpp View File

1
-/*269:*/
1
+/*279:*/
2
 #line 42 "./units.w"
2
 #line 42 "./units.w"
3
 
3
 
4
 #include "units.h"
4
 #include "units.h"
5
 #include <QtDebug> 
5
 #include <QtDebug> 
6
 
6
 
7
-/*:269*//*270:*/
7
+/*:279*//*280:*/
8
 #line 53 "./units.w"
8
 #line 53 "./units.w"
9
 
9
 
10
 bool Units::isTemperatureUnit(Unit unit)
10
 bool Units::isTemperatureUnit(Unit unit)
15
 unit==Rankine);
15
 unit==Rankine);
16
 }
16
 }
17
 
17
 
18
-/*:270*//*271:*/
18
+/*:280*//*281:*/
19
 #line 71 "./units.w"
19
 #line 71 "./units.w"
20
 
20
 
21
 double Units::convertTemperature(double value,Unit fromUnit,Unit toUnit)
21
 double Units::convertTemperature(double value,Unit fromUnit,Unit toUnit)
113
 return 0;
113
 return 0;
114
 }
114
 }
115
 
115
 
116
-/*:271*//*272:*/
116
+/*:281*//*282:*/
117
 #line 169 "./units.w"
117
 #line 169 "./units.w"
118
 
118
 
119
 double Units::convertRelativeTemperature(double value,Unit fromUnit,Unit toUnit)
119
 double Units::convertRelativeTemperature(double value,Unit fromUnit,Unit toUnit)
211
 return 0;
211
 return 0;
212
 }
212
 }
213
 
213
 
214
-/*:272*//*273:*/
214
+/*:282*//*283:*/
215
 #line 267 "./units.w"
215
 #line 267 "./units.w"
216
 
216
 
217
 double Units::convertWeight(double value,Unit fromUnit,Unit toUnit)
217
 double Units::convertWeight(double value,Unit fromUnit,Unit toUnit)
316
 unit==Gram);
316
 unit==Gram);
317
 }
317
 }
318
 
318
 
319
-/*:273*/
319
+/*:283*/

+ 2
- 2
src/units.h View File

1
-/*268:*/
1
+/*278:*/
2
 #line 8 "./units.w"
2
 #line 8 "./units.w"
3
 
3
 
4
 #include <QObject> 
4
 #include <QObject> 
32
 
32
 
33
 #endif
33
 #endif
34
 
34
 
35
-/*:268*/
35
+/*:278*/

+ 5
- 5
src/webelement.cpp View File

1
-/*563:*/
1
+/*573:*/
2
 #line 368 "./webview.w"
2
 #line 368 "./webview.w"
3
 
3
 
4
 #include "webelement.h"
4
 #include "webelement.h"
5
 
5
 
6
-/*561:*/
6
+/*571:*/
7
 #line 311 "./webview.w"
7
 #line 311 "./webview.w"
8
 
8
 
9
 TypicaWebElement::TypicaWebElement(QWebElement element):e(element)
9
 TypicaWebElement::TypicaWebElement(QWebElement element):e(element)
11
 
11
 
12
 }
12
 }
13
 
13
 
14
-/*:561*//*562:*/
14
+/*:571*//*572:*/
15
 #line 320 "./webview.w"
15
 #line 320 "./webview.w"
16
 
16
 
17
 void TypicaWebElement::appendInside(const QString&markup)
17
 void TypicaWebElement::appendInside(const QString&markup)
59
 e.setPlainText(text);
59
 e.setPlainText(text);
60
 }
60
 }
61
 
61
 
62
-/*:562*/
62
+/*:572*/
63
 #line 371 "./webview.w"
63
 #line 371 "./webview.w"
64
 
64
 
65
 
65
 
66
-/*:563*/
66
+/*:573*/

+ 2
- 2
src/webelement.h View File

1
-/*556:*/
1
+/*566:*/
2
 #line 248 "./webview.w"
2
 #line 248 "./webview.w"
3
 
3
 
4
 #include <QWebElement> 
4
 #include <QWebElement> 
27
 
27
 
28
 #endif
28
 #endif
29
 
29
 
30
-/*:556*/
30
+/*:566*/

+ 11
- 11
src/webview.cpp View File

1
-/*541:*/
1
+/*551:*/
2
 #line 50 "./webview.w"
2
 #line 50 "./webview.w"
3
 
3
 
4
 #include "webview.h"
4
 #include "webview.h"
5
 
5
 
6
-/*542:*/
6
+/*552:*/
7
 #line 57 "./webview.w"
7
 #line 57 "./webview.w"
8
 
8
 
9
 TypicaWebView::TypicaWebView():QWebView()
9
 TypicaWebView::TypicaWebView():QWebView()
12
 connect(page(),SIGNAL(linkClicked(QUrl)),this,SLOT(linkDelegate(QUrl)));
12
 connect(page(),SIGNAL(linkClicked(QUrl)),this,SLOT(linkDelegate(QUrl)));
13
 }
13
 }
14
 
14
 
15
-/*:542*//*543:*/
15
+/*:552*//*553:*/
16
 #line 73 "./webview.w"
16
 #line 73 "./webview.w"
17
 
17
 
18
 void TypicaWebView::linkDelegate(const QUrl&url)
18
 void TypicaWebView::linkDelegate(const QUrl&url)
20
 if(url.scheme()=="typica")
20
 if(url.scheme()=="typica")
21
 {
21
 {
22
 QString address(url.toEncoded());
22
 QString address(url.toEncoded());
23
-/*544:*/
23
+/*554:*/
24
 #line 91 "./webview.w"
24
 #line 91 "./webview.w"
25
 
25
 
26
 if(address=="typica://aboutqt")
26
 if(address=="typica://aboutqt")
29
 return;
29
 return;
30
 }
30
 }
31
 
31
 
32
-/*:544*/
32
+/*:554*/
33
 #line 79 "./webview.w"
33
 #line 79 "./webview.w"
34
 
34
 
35
-/*545:*/
35
+/*555:*/
36
 #line 100 "./webview.w"
36
 #line 100 "./webview.w"
37
 
37
 
38
 if(address.startsWith("typica://script/"))
38
 if(address.startsWith("typica://script/"))
41
 return;
41
 return;
42
 }
42
 }
43
 
43
 
44
-/*:545*/
44
+/*:555*/
45
 #line 80 "./webview.w"
45
 #line 80 "./webview.w"
46
 
46
 
47
 }
47
 }
51
 }
51
 }
52
 }
52
 }
53
 
53
 
54
-/*:543*//*546:*/
54
+/*:553*//*556:*/
55
 #line 112 "./webview.w"
55
 #line 112 "./webview.w"
56
 
56
 
57
 void TypicaWebView::load(const QString&url)
57
 void TypicaWebView::load(const QString&url)
88
 return page()->currentFrame()->documentElement().toOuterXml();
88
 return page()->currentFrame()->documentElement().toOuterXml();
89
 }
89
 }
90
 
90
 
91
-/*:546*//*552:*/
91
+/*:556*//*562:*/
92
 #line 205 "./webview.w"
92
 #line 205 "./webview.w"
93
 
93
 
94
 QWebElement TypicaWebView::documentElement()
94
 QWebElement TypicaWebView::documentElement()
101
 return page()->mainFrame()->findFirstElement(selector);
101
 return page()->mainFrame()->findFirstElement(selector);
102
 }
102
 }
103
 
103
 
104
-/*:552*/
104
+/*:562*/
105
 #line 53 "./webview.w"
105
 #line 53 "./webview.w"
106
 
106
 
107
 
107
 
108
-/*:541*/
108
+/*:551*/

+ 2
- 2
src/webview.h View File

1
-/*540:*/
1
+/*550:*/
2
 #line 14 "./webview.w"
2
 #line 14 "./webview.w"
3
 
3
 
4
 #include <QWebView> 
4
 #include <QWebView> 
34
 
34
 
35
 #endif
35
 #endif
36
 
36
 
37
-/*:540*/
37
+/*:550*/

+ 11
- 31
web/output/download-mac-latest.html View File

16
 	<div id="maintext">
16
 	<div id="maintext">
17
 <h1>Thank you for downloading Typica.</h1>
17
 <h1>Thank you for downloading Typica.</h1>
18
 <p>Your download from:</p>
18
 <p>Your download from:</p>
19
-<p><a href="http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.dmg">http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.dmg</a></p>
19
+<p><a href="http://www.randomfield.com/programs/typica/1.7/Typica-1.7.dmg">http://www.randomfield.com/programs/typica/1.7/Typica-1.7.dmg</a></p>
20
 <p>should begin automatically. If it does not,
20
 <p>should begin automatically. If it does not,
21
 please click the above link.</p>
21
 please click the above link.</p>
22
 <h2>Please Help Support Ongoing Development</h2>
22
 <h2>Please Help Support Ongoing Development</h2>
23
-<p>This will likely be one of the last releases in the 1.x
24
-series. Typica 1.0 was released in 2007 and I've learned a
25
-lot from developing this, using it, and having conversations
26
-with others about how they're using this. While there is the
27
-possibility of further improvements, I believe Typica has
28
-reached a point of maturity with a feature set appropriate for
29
-many coffee roasting firms.</p>
30
-<p>There is a large set of features and extensions that I have
31
-avoided implementing because the changes would have been too
32
-intrusive for the code base as it currently exists, but that
33
-feature set has become too compelling for me to leave undone.
34
-I am, therefore, attempting to spend as much time as I can on
35
-the development of Typica 2.0. This removes many of the legacy
36
-considerations and modernizes the tools and techniques used. I
37
-hope that the result is a better and more broadly useful Typica
38
-that can be maintained for another several years.</p>
39
 <p>While Typica is free and will continue to be made freely
23
 <p>While Typica is free and will continue to be made freely
40
 available complete with all source code, there are still costs
24
 available complete with all source code, there are still costs
41
 associated with ongoing development and keeping the software
25
 associated with ongoing development and keeping the software
51
 	</ul>
35
 	</ul>
52
 There is also a huge commitment of time in developing, testing,
36
 There is also a huge commitment of time in developing, testing,
53
 and supporting this.</p>
37
 and supporting this.</p>
54
-<p>Financial support to date does not come close to covering
55
-these costs, nor has it been enough that I could consider
56
-hiring people to improve the pace and quality of ongoing
57
-development. This is fine. I have no intention of halting work
58
-on the project as my work has benefitted greatly from having
59
-this software and I expect even greater benefit from the next
60
-major release. Personally I find the new feature set exciting
61
-to work on and am willing to cover funding shortfalls when I
62
-can afford to do so.</p>
63
-<p>That said, if you're finding this software to be useful in
64
-your business, if you've had a good support experience, or if
38
+<p>While Typica is for many uses a mature and reliable program,
39
+there are still a number of areas where the program can be
40
+improved and additional areas where I think it makes a lot of
41
+sense to extend the program. The limited funding for this work
42
+does slow progress. If you're finding this software to be useful
43
+in your business, if you've had a good support experience, or if
65
 you'd like to help me continue to develop this software, please
44
 you'd like to help me continue to develop this software, please
66
-consider providing some financial support to:</p>
45
+consider providing some financial support. Checks can be sent to:
67
 <p>Neal Wilson<br />
46
 <p>Neal Wilson<br />
68
 c/o Wilson's Coffee &amp; Tea<br />
47
 c/o Wilson's Coffee &amp; Tea<br />
69
 3306 Washington Ave.<br />
48
 3306 Washington Ave.<br />
70
 Racine, WI 53405<br />
49
 Racine, WI 53405<br />
71
 USA</p>
50
 USA</p>
51
+<p>or you can visit my <a href="http://youtube.com/users/N3Roaster">YouTube
52
+channel</a> and click the blue Support button on the right.</p>
72
 <p>Another great way to help Typica right now is to reach out
53
 <p>Another great way to help Typica right now is to reach out
73
 to your professional colleagues and let them know that you're
54
 to your professional colleagues and let them know that you're
74
 using Typica and what you like about it.</p>
55
 using Typica and what you like about it.</p>
75
-<p>Please note that I am unable to accept money through PayPal.</p>
76
-<iframe width="1" height="1" frameborder="0" src="http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.dmg"></iframe>
56
+<iframe width="1" height="1" frameborder="0" src="http://www.randomfield.com/programs/typica/1.7/Typica-1.7.dmg"></iframe>
77
 </div></div></body></html>
57
 </div></div></body></html>

+ 11
- 31
web/output/download-windows-latest.html View File

16
 	<div id="maintext">
16
 	<div id="maintext">
17
 <h1>Thank you for downloading Typica.</h1>
17
 <h1>Thank you for downloading Typica.</h1>
18
 <p>Your download from:</p>
18
 <p>Your download from:</p>
19
-<p><a href="http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.zip">http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.zip</a></p>
19
+<p><a href="http://www.randomfield.com/programs/typica/1.7/Typica-1.7.zip">http://www.randomfield.com/programs/typica/1.7/Typica-1.7.zip</a></p>
20
 <p>should begin automatically. If it does not,
20
 <p>should begin automatically. If it does not,
21
 please click the above link.</p>
21
 please click the above link.</p>
22
 <h2>Please Help Support Ongoing Development</h2>
22
 <h2>Please Help Support Ongoing Development</h2>
23
-<p>This will likely be one of the last releases in the 1.x
24
-series. Typica 1.0 was released in 2007 and I've learned a
25
-lot from developing this, using it, and having conversations
26
-with others about how they're using this. While there is the
27
-possibility of further improvements, I believe Typica has
28
-reached a point of maturity with a feature set appropriate for
29
-many coffee roasting firms.</p>
30
-<p>There is a large set of features and extensions that I have
31
-avoided implementing because the changes would have been too
32
-intrusive for the code base as it currently exists, but that
33
-feature set has become too compelling for me to leave undone.
34
-I am, therefore, attempting to spend as much time as I can on
35
-the development of Typica 2.0. This removes many of the legacy
36
-considerations and modernizes the tools and techniques used. I
37
-hope that the result is a better and more broadly useful Typica
38
-that can be maintained for another several years.</p>
39
 <p>While Typica is free and will continue to be made freely
23
 <p>While Typica is free and will continue to be made freely
40
 available complete with all source code, there are still costs
24
 available complete with all source code, there are still costs
41
 associated with ongoing development and keeping the software
25
 associated with ongoing development and keeping the software
51
 	</ul>
35
 	</ul>
52
 There is also a huge commitment of time in developing, testing,
36
 There is also a huge commitment of time in developing, testing,
53
 and supporting this.</p>
37
 and supporting this.</p>
54
-<p>Financial support to date does not come close to covering
55
-these costs, nor has it been enough that I could consider
56
-hiring people to improve the pace and quality of ongoing
57
-development. This is fine. I have no intention of halting work
58
-on the project as my work has benefitted greatly from having
59
-this software and I expect even greater benefit from the next
60
-major release. Personally I find the new feature set exciting
61
-to work on and am willing to cover funding shortfalls when I
62
-can afford to do so.</p>
63
-<p>That said, if you're finding this software to be useful in
64
-your business, if you've had a good support experience, or if
38
+<p>While Typica is for many uses a mature and reliable program,
39
+there are still a number of areas where the program can be
40
+improved and additional areas where I think it makes a lot of
41
+sense to extend the program. The limited funding for this work
42
+does slow progress. If you're finding this software to be useful
43
+in your business, if you've had a good support experience, or if
65
 you'd like to help me continue to develop this software, please
44
 you'd like to help me continue to develop this software, please
66
-consider providing some financial support to:</p>
45
+consider providing some financial support. Checks can be sent to:
67
 <p>Neal Wilson<br />
46
 <p>Neal Wilson<br />
68
 c/o Wilson's Coffee &amp; Tea<br />
47
 c/o Wilson's Coffee &amp; Tea<br />
69
 3306 Washington Ave.<br />
48
 3306 Washington Ave.<br />
70
 Racine, WI 53405<br />
49
 Racine, WI 53405<br />
71
 USA</p>
50
 USA</p>
51
+<p>or you can visit my <a href="http://youtube.com/users/N3Roaster">YouTube
52
+channel</a> and click the blue Support button on the right.</p>
72
 <p>Another great way to help Typica right now is to reach out
53
 <p>Another great way to help Typica right now is to reach out
73
 to your professional colleagues and let them know that you're
54
 to your professional colleagues and let them know that you're
74
 using Typica and what you like about it.</p>
55
 using Typica and what you like about it.</p>
75
-<p>Please note that I am unable to accept money through PayPal.</p>
76
-<iframe width="1" height="1" frameborder="0" src="http://www.randomfield.com/programs/typica/1.6/Typica-1.6.4.zip"></iframe>
56
+<iframe width="1" height="1" frameborder="0" src="http://www.randomfield.com/programs/typica/1.7/Typica-1.7.zip"></iframe>
77
 </div></div></body></html>
57
 </div></div></body></html>

+ 3
- 3
web/output/downloads.html View File

14
 	</div>
14
 	</div>
15
 	</div>
15
 	</div>
16
 	<div id="maintext">
16
 	<div id="maintext">
17
-<p>The latest release of Typica is version 1.6.4, released June 7, 2015.
17
+<p>The latest release of Typica is version 1.7, released January 24, 2016.
18
 This is available for Microsoft Windows, and Intel/Mac OS X.</p>
18
 This is available for Microsoft Windows, and Intel/Mac OS X.</p>
19
-<p><a href="download-mac-latest.html">Typica 1.6.4 for Intel/Mac OS X</a></p>
20
-<p><a href="download-windows-latest.html">Typica 1.6.4 for Microsoft Windows</a></p>
19
+<p><a href="download-mac-latest.html">Typica 1.7 for Intel/Mac OS X</a></p>
20
+<p><a href="download-windows-latest.html">Typica 1.7 for Microsoft Windows</a></p>
21
 <p>The latest source code can always be found on
21
 <p>The latest source code can always be found on
22
 <a href="https://github.com/N3Roaster/typica">GitHub</a>.</p>
22
 <a href="https://github.com/N3Roaster/typica">GitHub</a>.</p>
23
 </div></div></body></html>
23
 </div></div></body></html>

+ 8
- 13
web/output/index.html View File

1
 <html><head>
1
 <html><head>
2
 	<title>Typica - Data for Coffee Roasters</title>
2
 	<title>Typica - Data for Coffee Roasters</title>
3
 	<link rel="stylesheet" type="text/css" href="style.css">
3
 	<link rel="stylesheet" type="text/css" href="style.css">
4
-<meta name="twitter:card" content="summary_large_image">
5
-<meta name="twitter:site" content="@N3Roaster">
6
-<meta name="twitter:title" content="Typica 1.6.4 Now Available. Free Software for Coffee Roasters">
7
-<meta name="twitter:description" content="Typica is a cross-platform application for coffee roasters with features for recording your roasts, tracking green coffee inventory, product development, improving production consistency, and more.">
8
-<meta name="twitter:image" content="http://www.randomfield.com/programs/typica/release164tc.png">
9
 	</head><body><div id="page"><div id="topmatter">
4
 	</head><body><div id="page"><div id="topmatter">
10
 	<div id="topbanner">
5
 	<div id="topbanner">
11
 	<img src="logo96.png" height="96px" width="96px" alt="Typica logo" />
6
 	<img src="logo96.png" height="96px" width="96px" alt="Typica logo" />
25
 and more.</p>
20
 and more.</p>
26
 <p>The program is free open source software available under the
21
 <p>The program is free open source software available under the
27
 MIT license.</p>
22
 MIT license.</p>
28
-<h1>Typica 1.6.4 Now Available</h1>
29
-<p>The latest release of Typica includes bug fixes and minor
30
-feature updates. The most significant new feature is the
31
-ability to use three new types of timer in addition to the
32
-familiar batch timer. See the documentation for details on how to
33
-configure these or watch this new video describing each of the
34
-new timer types and why you might want to use them.</p>
35
-<iframe width="640" height="360" src="https://www.youtube.com/embed/2zt9XlQd9oY" frameborder="0" allowfullscreen></iframe>
23
+<h1>Typica 1.7 Now Available</h1>
24
+<iframe width="640" height="360" src="https://www.youtube.com/embed/U7xTefVLRfk" frameborder="0" allowfullscreen></iframe>
25
+<p>The latest release of Typica includes several new features
26
+such as support for more detailed roast specifications, ability
27
+to set reminders based on production, a new batch log with search
28
+and filter features, and more. This also marks the first release
29
+that includes a translation of the program to another language.
30
+People using a computer with German locale settings will see that.</p>
36
 <h1>Data Acquisition</h1>
31
 <h1>Data Acquisition</h1>
37
 <p>Every batch of coffee you roast with Typica can be recorded for
32
 <p>Every batch of coffee you roast with Typica can be recorded for
38
 future reference. A practically unlimited number of target roast
33
 future reference. A practically unlimited number of target roast

+ 0
- 0
web/src/pages/download-mac-latest.m4 View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save