Browse Source

Merge branch 'release-1.6.2'

Neal Wilson 10 years ago
parent
commit
b386790a8d

+ 19
- 4
config/Reports/auco.xml View File

65
                 output.writeStartElement("thead");
65
                 output.writeStartElement("thead");
66
                 output.writeStartElement("tr");
66
                 output.writeStartElement("tr");
67
                 output.writeStartElement("th");
67
                 output.writeStartElement("th");
68
-                output.writeAttribute("colspan", "5");
68
+                output.writeAttribute("colspan", "8");
69
                 output.writeCharacters("Regular Coffees");
69
                 output.writeCharacters("Regular Coffees");
70
                 output.writeEndElement();
70
                 output.writeEndElement();
71
                 output.writeEndElement();
71
                 output.writeEndElement();
75
                 output.writeTextElement("th", "Avg. Cost");
75
                 output.writeTextElement("th", "Avg. Cost");
76
 				output.writeTextElement("th", "Last Cost");
76
 				output.writeTextElement("th", "Last Cost");
77
 				output.writeTextElement("th", "Last Purchase Date");
77
 				output.writeTextElement("th", "Last Purchase Date");
78
+				output.writeTextElement("th", "Bag Size (min)");
79
+				output.writeTextElement("th", "Bag Size (max)");
80
+				output.writeTextElement("th", "Bag Size (mean)");
78
                 output.writeEndElement();
81
                 output.writeEndElement();
79
                 output.writeEndElement();
82
                 output.writeEndElement();
80
                 output.writeStartElement("tbody");
83
                 output.writeStartElement("tbody");
105
 						orderClause = "cost DESC";
108
 						orderClause = "cost DESC";
106
 						break;
109
 						break;
107
 				}
110
 				}
108
-				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)) FROM coffee_history WHERE id IN (SELECT id FROM regular_coffees) GROUP BY origin ORDER BY " + orderClause);
111
+				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);
109
 				query.bind(":conversion", conversion);
112
 				query.bind(":conversion", conversion);
110
 				query.bind(":conversion2", conversion);
113
 				query.bind(":conversion2", conversion);
111
 				query.bind(":conversion3", conversion);
114
 				query.bind(":conversion3", conversion);
115
+				query.bind(":conversion4", conversion);
116
+				query.bind(":conversion5", conversion);
117
+				query.bind(":conversion6", conversion);
112
 				query.exec();
118
 				query.exec();
113
                 while(query.next())
119
                 while(query.next())
114
                 {
120
                 {
118
                     output.writeTextElement("td", query.value(2));
124
                     output.writeTextElement("td", query.value(2));
119
 					output.writeTextElement("td", query.value(3));
125
 					output.writeTextElement("td", query.value(3));
120
 					output.writeTextElement("td", query.value(4));
126
 					output.writeTextElement("td", query.value(4));
127
+					output.writeTextElement("td", query.value(5));
128
+					output.writeTextElement("td", query.value(6));
129
+					output.writeTextElement("td", query.value(7));
121
                     output.writeEndElement();
130
                     output.writeEndElement();
122
                 }
131
                 }
123
                 output.writeStartElement("tr");
132
                 output.writeStartElement("tr");
124
                 output.writeStartElement("th");
133
                 output.writeStartElement("th");
125
-                output.writeAttribute("colspan", "5");
134
+                output.writeAttribute("colspan", "8");
126
                 output.writeCharacters("Decaffeinated Coffees");
135
                 output.writeCharacters("Decaffeinated Coffees");
127
                 output.writeEndElement();
136
                 output.writeEndElement();
128
                 output.writeEndElement();
137
                 output.writeEndElement();
129
-				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)) FROM coffee_history WHERE id IN (SELECT id FROM decaf_coffees) GROUP BY origin ORDER BY " + orderClause);
138
+				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);
130
 				query.bind(":conversion", conversion);
139
 				query.bind(":conversion", conversion);
131
 				query.bind(":conversion2", conversion);
140
 				query.bind(":conversion2", conversion);
132
 				query.bind(":conversion3", conversion);
141
 				query.bind(":conversion3", conversion);
142
+				query.bind(":conversion4", conversion);
143
+				query.bind(":conversion5", conversion);
144
+				query.bind(":conversion6", conversion);
133
 				query.exec();
145
 				query.exec();
134
                 while(query.next())
146
                 while(query.next())
135
                 {
147
                 {
139
                     output.writeTextElement("td", query.value(2));
151
                     output.writeTextElement("td", query.value(2));
140
 					output.writeTextElement("td", query.value(3));
152
 					output.writeTextElement("td", query.value(3));
141
 					output.writeTextElement("td", query.value(4));
153
 					output.writeTextElement("td", query.value(4));
154
+					output.writeTextElement("td", query.value(5));
155
+					output.writeTextElement("td", query.value(6));
156
+					output.writeTextElement("td", query.value(7));
142
                     output.writeEndElement();
157
                     output.writeEndElement();
143
                 }
158
                 }
144
 				query = query.invalidate();
159
 				query = query.invalidate();

+ 12
- 17
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
-            <label>Start Date:</label>
6
-            <calendar id="startdate" />
7
-            <label>End Date:</label>
8
-            <calendar id="enddate" />
5
+			<daterange id="dates" initial="19" /><!-- Current Year to Date -->
9
             <label>Days to Average</label>
6
             <label>Days to Average</label>
10
             <line validator="integer" id="days">7</line>
7
             <line validator="integer" id="days">7</line>
11
 			<label>Weight Unit:</label>
8
 			<label>Weight Unit:</label>
20
     <program>
17
     <program>
21
         <![CDATA[
18
         <![CDATA[
22
             this.windowTitle = "Typica - Previous Year Production Comparison";
19
             this.windowTitle = "Typica - Previous Year Production Comparison";
23
-            var startDateField = findChildObject(this, 'startdate');
24
-            startDateField.setDate(startDateField.year(), 1, 1);
25
-            var endDateField = findChildObject(this, 'enddate');
20
+			var dateSelect = findChildObject(this, 'dates');
21
+			dateSelect.removeIndex(23); // Remove Lifetime range
26
             var view = findChildObject(this, 'report');
22
             var view = findChildObject(this, 'report');
27
             var printMenu = findChildObject(this, 'print');
23
             var printMenu = findChildObject(this, 'print');
28
             printMenu.triggered.connect(function() {
24
             printMenu.triggered.connect(function() {
79
                 output.writeStartElement("tbody");
75
                 output.writeStartElement("tbody");
80
                 var query = new QSqlQuery();
76
                 var query = new QSqlQuery();
81
                 query.exec("START TRANSACTION");
77
                 query.exec("START TRANSACTION");
82
-                var curStartDate = "'"+startDateField.year()+"-"+startDateField.month()+"-"+startDateField.day()+"'";
83
-                query.exec("SELECT "+curStartDate+"::date - interval '1 year', '"+endDateField.year()+"-"+endDateField.month()+"-"+endDateField.day()+"'::date - interval '1 year' + interval '1 day', '"+endDateField.year()+"-"+endDateField.month()+"-"+endDateField.day()+"'::date + interval '1 day'");
84
-                query.next();
85
-                var curEndDate = "'"+query.value(2)+"'";
78
+				var dateRange = dateSelect.currentRange();
79
+				var curStartDate = "'"+dateRange[0]+"'";
80
+				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'");
82
+				query.next();
83
+                curEndDate = "'"+query.value(2)+"'";
86
                 var prevStartDate = "'"+query.value(0)+"'";
84
                 var prevStartDate = "'"+query.value(0)+"'";
87
                 var prevEndDate = "'"+query.value(1)+"'";
85
                 var prevEndDate = "'"+query.value(1)+"'";
88
                 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";
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";
501
                 buffer.close();
499
                 buffer.close();
502
             }
500
             }
503
             refresh();
501
             refresh();
504
-            startDateField.dateChanged.connect(function() {
505
-                refresh();
506
-            });
507
-            endDateField.dateChanged.connect(function() {
508
-                refresh();
509
-            });
502
+			dateSelect.rangeUpdated.connect(function() {
503
+				refresh();
504
+			});
510
 			avgField.editingFinished.connect(function() {
505
 			avgField.editingFinished.connect(function() {
511
 				refresh();
506
 				refresh();
512
 			});
507
 			});

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

2
 	<reporttitle>Purchase:->Coffee Purchase Previous Years Comparison</reporttitle>
2
 	<reporttitle>Purchase:->Coffee Purchase Previous Years Comparison</reporttitle>
3
 	<layout type="vertical">
3
 	<layout type="vertical">
4
 		<layout type="horizontal">
4
 		<layout type="horizontal">
5
-			<label>Start Date:</label>
6
-			<calendar id="startdate" />
7
-			<label>End Date:</label>
8
-			<calendar id="enddate" />
5
+			<daterange id = "dates" initial="23" /><!--Lifetime-->
9
 			<label>Weight Unit:</label>
6
 			<label>Weight Unit:</label>
10
 			<sqldrop id="unit" />
7
 			<sqldrop id="unit" />
11
 			<stretch />
8
 			<stretch />
18
 	<program>
15
 	<program>
19
 		<![CDATA[
16
 		<![CDATA[
20
 			this.windowTitle = "Typica - Coffee Purchase Previous Years Comparison";
17
 			this.windowTitle = "Typica - Coffee Purchase Previous Years Comparison";
21
-			/* Set starting year to the first year on record. */
22
-			var startDateField = findChildObject(this, 'startdate');
18
+			/* Set Lifetime range. */
19
+			var dateSelect = findChildObject(this, 'dates');
23
 			var query = new QSqlQuery();
20
 			var query = new QSqlQuery();
24
-			query.exec("SELECT EXTRACT(YEAR FROM time) FROM purchase WHERE time = (SELECT min(time) FROM purchase)");
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')");
25
 			query.next();
22
 			query.next();
26
-			startDateField.setDate(query.value(0), 1, 1);
23
+			var lifetimeStartDate = query.value(0);
24
+			query.next();
25
+			var lifetimeEndDate = query.value(0);
26
+			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
27
 			query = query.invalidate();
27
 			query = query.invalidate();
28
-			/* Set ending year to the current year. */
29
-			var endDateField = findChildObject(this, 'enddate');
30
-			endDateField.setDate(endDateField.year(), 12, 31);
31
 			/* Enable printing */
28
 			/* Enable printing */
32
 			var view = findChildObject(this, 'report');
29
 			var view = findChildObject(this, 'report');
33
 			var printMenu = findChildObject(this, 'print');
30
 			var printMenu = findChildObject(this, 'print');
81
 				var unittotal = 0;
78
 				var unittotal = 0;
82
 				var costtotal = 0;
79
 				var costtotal = 0;
83
 				var query = new QSqlQuery();
80
 				var query = new QSqlQuery();
84
-				for(var i = startDateField.year(); i <= endDateField.year(); i++)
81
+				var dateRange = dateSelect.currentRange();
82
+				var startYear = Number(dateRange[0].substr(0, 4));
83
+				var endYear = Number(dateRange[dateRange.length - 1].substr(0, 4));
84
+				for(var i = startYear; i <= endYear; i++)
85
 				{
85
 				{
86
 					output.writeStartElement("tr");
86
 					output.writeStartElement("tr");
87
 					output.writeAttribute("id", "y"+i);
87
 					output.writeAttribute("id", "y"+i);
121
 			}
121
 			}
122
 			refresh();
122
 			refresh();
123
 			/* Update report as needed. */
123
 			/* Update report as needed. */
124
-			startDateField.dateChanged.connect(function() {
125
-				refresh();
126
-			});
127
-			endDateField.dateChanged.connect(function() {
124
+			dateSelect.rangeUpdated.connect(function() {
128
 				refresh();
125
 				refresh();
129
 			});
126
 			});
130
 			/* Expand year data */
127
 			/* Expand year data */

+ 43
- 20
config/Reports/invchange.xml View File

2
 	<reporttitle>Inventory:->Inventory Change Summary</reporttitle>
2
 	<reporttitle>Inventory:->Inventory Change Summary</reporttitle>
3
 	<layout type="vertical">
3
 	<layout type="vertical">
4
 		<layout type="horizontal">
4
 		<layout type="horizontal">
5
-			<label>Start Date:</label>
6
-			<calendar id="startdate" />
7
-			<label>End Date:</label>
8
-			<calendar id="enddate" />
5
+			<daterange id="dates" initial="19" /><!-- Current Year to Date-->
9
 			<label>Weight Unit:</label>
6
 			<label>Weight Unit:</label>
10
 			<sqldrop id="unit" />
7
 			<sqldrop id="unit" />
11
 			<stretch />
8
 			<stretch />
18
 	<program>
15
 	<program>
19
 		<![CDATA[
16
 		<![CDATA[
20
 			this.windowTitle = "Typica - Inventory Change Summary";
17
 			this.windowTitle = "Typica - Inventory Change Summary";
21
-			var startDateField = findChildObject(this, 'startdate');
22
-			startDateField.setDate(startDateField.year(), 1, 1);
23
-			var endDateField = findChildObject(this, 'enddate');
18
+			var dateSelect = findChildObject(this, 'dates');
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");
21
+			dateQuery.next();
22
+			var lifetimeStartDate = dateQuery.value(0);
23
+			var lifetimeEndDate;
24
+			if(dateQuery.next()) {
25
+				lifetimeEndDate = dateQuery.value(0);
26
+			} else {
27
+				lifetimeEndDate = lifetimeStartDate;
28
+			}
29
+			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
30
+			dateQuery = dateQuery.invalidate();
24
 			var unitBox = findChildObject(this, 'unit');
31
 			var unitBox = findChildObject(this, 'unit');
25
 			unitBox.addItem("Kg");
32
 			unitBox.addItem("Kg");
26
 			unitBox.addItem("Lb");
33
 			unitBox.addItem("Lb");
46
 				output.writeTextElement("title", "Inventory Change Summary");
53
 				output.writeTextElement("title", "Inventory Change Summary");
47
 				output.writeEndElement();
54
 				output.writeEndElement();
48
 				output.writeStartElement("body");
55
 				output.writeStartElement("body");
49
-				var startDate = "" + startDateField.year() + "-" + startDateField.month() + "-" + startDateField.day();
50
-				var endDate = "" + endDateField.year() + "-" + endDateField.month() + "-" + endDateField.day();
56
+				var dateRange = dateSelect.currentRange();
57
+				var startDate = dateRange[0];
58
+				var endDate = dateRange[dateRange.length - 1];
51
 				output.writeTextElement("h1", "Inventory Change Summary: " + startDate + " – " + endDate);
59
 				output.writeTextElement("h1", "Inventory Change Summary: " + startDate + " – " + endDate);
52
 				var conversion = 1;
60
 				var conversion = 1;
53
 				if(unitBox.currentIndex == 0) {
61
 				if(unitBox.currentIndex == 0) {
58
 					unitText = "Kg";
66
 					unitText = "Kg";
59
 				}
67
 				}
60
 				var query = new QSqlQuery();
68
 				var query = new QSqlQuery();
61
-					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)*:c6 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 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)/: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";
62
 				query.prepare(q);
70
 				query.prepare(q);
63
 				query.bind(":sd1", startDate);
71
 				query.bind(":sd1", startDate);
64
 				query.bind(":sd2", startDate);
72
 				query.bind(":sd2", startDate);
133
 					output.writeEndElement();
141
 					output.writeEndElement();
134
 					output.writeTextElement("td", query.value(1)); //Coffee
142
 					output.writeTextElement("td", query.value(1)); //Coffee
135
 					output.writeTextElement("td", query.value(2)); //Reference
143
 					output.writeTextElement("td", query.value(2)); //Reference
136
-					output.writeTextElement("td", parseFloat(query.value(3)).toFixed(2)); //Starting Wt
144
+					output.writeStartElement("td"); //Starting Wt
145
+					output.writeAttribute("title", (parseFloat(query.value(3))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
146
+					output.writeCDATA(parseFloat(query.value(3)).toFixed(2));
147
+					output.writeEndElement(); //End of Starting Wt.
137
 					output.writeTextElement("td", parseFloat(query.value(10)).toFixed(2)); //Starting Cost
148
 					output.writeTextElement("td", parseFloat(query.value(10)).toFixed(2)); //Starting Cost
138
-					output.writeTextElement("td", parseFloat(query.value(4)).toFixed(2)); //Purchase Wt
149
+					output.writeStartElement("td"); //Purchase Wt
150
+					output.writeAttribute("title", (parseFloat(query.value(4))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
151
+					output.writeCDATA(parseFloat(query.value(4)).toFixed(2));
152
+					output.writeEndElement(); //End of Purchase Wt
139
 					output.writeTextElement("td", parseFloat(query.value(11)).toFixed(2)); //Purchase Cost
153
 					output.writeTextElement("td", parseFloat(query.value(11)).toFixed(2)); //Purchase Cost
140
-					output.writeTextElement("td", parseFloat(query.value(5)).toFixed(2)); //Use Wt
154
+					output.writeStartElement("td"); //Use Wt
155
+					output.writeAttribute("title", (parseFloat(query.value(5))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
156
+					output.writeCDATA(parseFloat(query.value(5)).toFixed(2));
157
+					output.writeEndElement(); //End of Use Wt
141
 					output.writeTextElement("td", parseFloat(query.value(12)).toFixed(2)); //Use Cost
158
 					output.writeTextElement("td", parseFloat(query.value(12)).toFixed(2)); //Use Cost
142
-					output.writeTextElement("td", parseFloat(query.value(6)).toFixed(2)); //Sale Wt
159
+					output.writeStartElement("td"); //Sale Wt
160
+					output.writeAttribute("title", (parseFloat(query.value(6))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
161
+					output.writeCDATA(parseFloat(query.value(6)).toFixed(2));
162
+					output.writeEndElement(); //End of Sale Wt
143
 					output.writeTextElement("td", parseFloat(query.value(13)).toFixed(2)); //Sale Cost
163
 					output.writeTextElement("td", parseFloat(query.value(13)).toFixed(2)); //Sale Cost
144
-					output.writeTextElement("td", parseFloat(query.value(9)).toFixed(2)); //Adjustment Wt
164
+					output.writeStartElement("td"); //Adjustment Wt
165
+					output.writeAttribute("title", (parseFloat(query.value(9))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
166
+					output.writeCDATA(parseFloat(query.value(9)).toFixed(2));
167
+					output.writeEndElement(); //Adjustment Wt
145
 					output.writeTextElement("td", parseFloat(query.value(15)).toFixed(2)); //Adjustment Cost
168
 					output.writeTextElement("td", parseFloat(query.value(15)).toFixed(2)); //Adjustment Cost
146
-					output.writeTextElement("td", parseFloat(query.value(7)).toFixed(2)); //Ending Wt
169
+					output.writeStartElement("td"); //Ending Wt
170
+					output.writeAttribute("title", (parseFloat(query.value(7))/parseFloat(query.value(16)) * 100).toFixed(0) + "%");
171
+					output.writeCDATA(parseFloat(query.value(7)).toFixed(2));
172
+					output.writeEndElement(); //End of Ending Wt
147
 					output.writeTextElement("td", parseFloat(query.value(14)).toFixed(2)); //Ending Cost
173
 					output.writeTextElement("td", parseFloat(query.value(14)).toFixed(2)); //Ending Cost
148
 					output.writeEndElement();
174
 					output.writeEndElement();
149
 					sum3 += parseFloat(query.value(3));
175
 					sum3 += parseFloat(query.value(3));
188
 				query = query.invalidate();
214
 				query = query.invalidate();
189
 			}
215
 			}
190
 			refresh();
216
 			refresh();
191
-			startDateField.dateChanged.connect(function() {
192
-				refresh();
193
-			});
194
-			endDateField.dateChanged.connect(function() {
217
+			dateSelect.rangeUpdated.connect(function() {
195
 				refresh();
218
 				refresh();
196
 			});
219
 			});
197
 			view.scriptLinkClicked.connect(function(url) {
220
 			view.scriptLinkClicked.connect(function(url) {

+ 4
- 0
config/Windows/greensales.xml View File

27
 			unitBox.addItem("Kg");
27
 			unitBox.addItem("Kg");
28
 			unitBox.addItem("oz");
28
 			unitBox.addItem("oz");
29
 			unitBox.addItem("lb");
29
 			unitBox.addItem("lb");
30
+			unitBox.currentIndex = (QSettings.value("script/greensales_unit", unitBox.findText("lb")));
31
+			unitBox['currentIndexChanged(int)'].connect(function() {
32
+				QSettings.setValue("script/greensales_unit", unitBox.currentIndex);
33
+			});
30
 			var convertToPounds = function(w, u) {
34
 			var convertToPounds = function(w, u) {
31
 				switch(u)
35
 				switch(u)
32
 				{
36
 				{

+ 4
- 2
src/Typica.pro View File

23
     webview.h \
23
     webview.h \
24
     webelement.h \
24
     webelement.h \
25
     scale.h \
25
     scale.h \
26
-    draglabel.h
26
+    draglabel.h \
27
+    daterangeselector.h
27
 SOURCES += typica.cpp \
28
 SOURCES += typica.cpp \
28
     helpmenu.cpp \
29
     helpmenu.cpp \
29
     abouttypica.cpp \
30
     abouttypica.cpp \
31
     webview.cpp \
32
     webview.cpp \
32
     webelement.cpp \
33
     webelement.cpp \
33
     scale.cpp \
34
     scale.cpp \
34
-    draglabel.cpp
35
+    draglabel.cpp \
36
+    daterangeselector.cpp
35
 
37
 
36
 RESOURCES += \
38
 RESOURCES += \
37
     resources.qrc
39
     resources.qrc

+ 1
- 1
src/abouttypica.cpp View File

17
 setCentralWidget(banner);
17
 setCentralWidget(banner);
18
 }
18
 }
19
 
19
 
20
-#line 5640 "./typica.w"
20
+#line 5641 "./typica.w"
21
 
21
 
22
 /*:223*/
22
 /*:223*/
23
 #line 36 "./abouttypica.w"
23
 #line 36 "./abouttypica.w"

+ 43
- 0
src/clock.cpp View File

1
+/*244:*/
2
+#line 52 "./clock.w"
3
+
4
+#include "clock.h"
5
+
6
+/*245:*/
7
+#line 61 "./clock.w"
8
+
9
+Clock::Clock():QObject(NULL)
10
+{
11
+reference.start();
12
+}
13
+
14
+Clock::~Clock()
15
+{
16
+
17
+}
18
+
19
+/*:245*//*246:*/
20
+#line 79 "./clock.w"
21
+
22
+qint64 Clock::timestamp()
23
+{
24
+guard.lock();
25
+qint64 retval= reference.elapsed();
26
+guard.unlock();
27
+emit newTime(retval);
28
+return retval;
29
+}
30
+
31
+void Clock::setEpoch()
32
+{
33
+guard.lock();
34
+reference.restart();
35
+guard.unlock();
36
+emit newTime(0);
37
+}
38
+
39
+/*:246*/
40
+#line 55 "./clock.w"
41
+
42
+
43
+/*:244*/

+ 29
- 0
src/clock.h View File

1
+/*243:*/
2
+#line 24 "./clock.w"
3
+
4
+#include <QElapsedTimer> 
5
+#include <QMutex> 
6
+#include <QObject> 
7
+
8
+#ifndef ClockHeader
9
+#define ClockHeader
10
+
11
+class Clock:public QObject
12
+{
13
+Q_OBJECT
14
+public:
15
+Clock();
16
+~Clock();
17
+qint64 timestamp();
18
+public slots:
19
+void setEpoch();
20
+signals:
21
+void newTime(qint64 time);
22
+private:
23
+QElapsedTimer reference;
24
+QMutex guard;
25
+};
26
+
27
+#endif
28
+
29
+/*:243*/

+ 3
- 3
src/dataqsdk.w View File

496
 	int rstart = finalizedPort.indexOf("COM");
496
 	int rstart = finalizedPort.indexOf("COM");
497
 	finalizedPort.remove(0, rstart + 3);
497
 	finalizedPort.remove(0, rstart + 3);
498
 	bool chopFinished = false;
498
 	bool chopFinished = false;
499
-	int finalizedPortNumber;
499
+	int finalizedPortNumber = 0;
500
 	while(finalizedPort.size() > 0 && !chopFinished)
500
 	while(finalizedPort.size() > 0 && !chopFinished)
501
 	{
501
 	{
502
 		finalizedPortNumber = finalizedPort.toInt(&chopFinished);
502
 		finalizedPortNumber = finalizedPort.toInt(&chopFinished);
1098
 @<DATAQ SDK device settings@>=
1098
 @<DATAQ SDK device settings@>=
1099
 bool autoSelect;
1099
 bool autoSelect;
1100
 QString deviceID;
1100
 QString deviceID;
1101
-int channelOfInterest;
1101
+unsigned int channelOfInterest;
1102
 
1102
 
1103
 @ This information is accessed through the reference element associated with
1103
 @ This information is accessed through the reference element associated with
1104
 the parent node of the current configuration and from the row number of the
1104
 the parent node of the current configuration and from the row number of the
1141
 	resetButton->setEnabled(true);
1141
 	resetButton->setEnabled(true);
1142
 	calibrationDevice = new DataqSdkDevice(deviceID);
1142
 	calibrationDevice = new DataqSdkDevice(deviceID);
1143
 	Channel *channel;
1143
 	Channel *channel;
1144
-	for(int i = 0; i <= channelOfInterest; i++)
1144
+	for(unsigned int i = 0; i <= channelOfInterest; i++)
1145
 	{
1145
 	{
1146
 		channel = calibrationDevice->newChannel(Units::Unitless);
1146
 		channel = calibrationDevice->newChannel(Units::Unitless);
1147
 	}
1147
 	}

+ 346
- 0
src/daterangeselector.cpp View File

1
+/*603:*/
2
+#line 66 "./daterangeselector.w"
3
+
4
+#include <QCalendarWidget> 
5
+#include <QPushButton> 
6
+#include <QBoxLayout> 
7
+#include <QLabel> 
8
+#include <QToolButton> 
9
+#include <QApplication> 
10
+#include <QDesktopWidget> 
11
+
12
+#include "daterangeselector.h"
13
+
14
+/*604:*/
15
+#line 86 "./daterangeselector.w"
16
+
17
+class CustomDateRangePopup:public QWidget
18
+{
19
+Q_OBJECT
20
+public:
21
+CustomDateRangePopup(QWidget*parent= NULL);
22
+public slots:
23
+void applyRange();
24
+signals:
25
+void hidingPopup();
26
+protected:
27
+virtual void hideEvent(QHideEvent*event);
28
+private slots:
29
+void validateRange();
30
+private:
31
+QCalendarWidget*startDateSelector;
32
+QCalendarWidget*endDateSelector;
33
+QPushButton*applyButton;
34
+};
35
+
36
+/*:604*/
37
+#line 77 "./daterangeselector.w"
38
+
39
+/*605:*/
40
+#line 110 "./daterangeselector.w"
41
+
42
+CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
43
+QWidget(parent,Qt::Popup),startDateSelector(new QCalendarWidget),
44
+endDateSelector(new QCalendarWidget),applyButton(new QPushButton(tr("Apply")))
45
+{
46
+setAttribute(Qt::WA_WindowPropagation);
47
+
48
+QVBoxLayout*outerLayout= new QVBoxLayout;
49
+QHBoxLayout*calendarsLayout= new QHBoxLayout;
50
+QVBoxLayout*startDateLayout= new QVBoxLayout;
51
+QVBoxLayout*endDateLayout= new QVBoxLayout;
52
+QHBoxLayout*buttonLayout= new QHBoxLayout;
53
+QLabel*startDateLabel= new QLabel(tr("From"));
54
+QLabel*endDateLabel= new QLabel(tr("To"));
55
+startDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
56
+endDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
57
+DateRangeSelector*selector= qobject_cast<DateRangeSelector*> (parent);
58
+if(parent){
59
+QStringList range= selector->currentRange().toStringList();
60
+startDateSelector->setSelectedDate(QDate::fromString(range.first(),Qt::ISODate));
61
+endDateSelector->setSelectedDate(QDate::fromString(range.last(),Qt::ISODate));
62
+}
63
+connect(startDateSelector,SIGNAL(selectionChanged()),this,SLOT(validateRange()));
64
+connect(endDateSelector,SIGNAL(selectionChanged()),this,SLOT(validateRange()));
65
+
66
+startDateLayout->addWidget(startDateLabel);
67
+startDateLayout->addWidget(startDateSelector);
68
+endDateLayout->addWidget(endDateLabel);
69
+endDateLayout->addWidget(endDateSelector);
70
+
71
+connect(applyButton,SIGNAL(clicked()),this,SLOT(applyRange()));
72
+
73
+buttonLayout->addStretch();
74
+buttonLayout->addWidget(applyButton);
75
+
76
+calendarsLayout->addLayout(startDateLayout);
77
+calendarsLayout->addLayout(endDateLayout);
78
+outerLayout->addLayout(calendarsLayout);
79
+outerLayout->addLayout(buttonLayout);
80
+setLayout(outerLayout);
81
+}
82
+
83
+/*:605*//*606:*/
84
+#line 158 "./daterangeselector.w"
85
+
86
+void CustomDateRangePopup::hideEvent(QHideEvent*)
87
+{
88
+emit hidingPopup();
89
+}
90
+
91
+/*:606*//*607:*/
92
+#line 167 "./daterangeselector.w"
93
+
94
+void CustomDateRangePopup::applyRange()
95
+{
96
+DateRangeSelector*selector= qobject_cast<DateRangeSelector*> (parentWidget());
97
+if(selector)
98
+{
99
+selector->setCustomRange(QVariant(QStringList()<<
100
+startDateSelector->selectedDate().toString(Qt::ISODate)<<
101
+endDateSelector->selectedDate().toString(Qt::ISODate)));
102
+}
103
+hide();
104
+}
105
+
106
+/*:607*//*608:*/
107
+#line 184 "./daterangeselector.w"
108
+
109
+void CustomDateRangePopup::validateRange()
110
+{
111
+if(startDateSelector->selectedDate()> endDateSelector->selectedDate())
112
+{
113
+applyButton->setEnabled(false);
114
+}
115
+else
116
+{
117
+applyButton->setEnabled(true);
118
+}
119
+}
120
+
121
+/*:608*/
122
+#line 78 "./daterangeselector.w"
123
+
124
+/*609:*/
125
+#line 202 "./daterangeselector.w"
126
+
127
+DateRangeSelector::DateRangeSelector(QWidget*parent):
128
+QWidget(parent),quickSelector(new QComboBox(this)),
129
+customRangeSelector(NULL),lastIndex(0)
130
+{
131
+connect(quickSelector,SIGNAL(currentIndexChanged(int)),this,SLOT(updateRange(int)));
132
+
133
+QDate currentDate= QDate::currentDate();
134
+
135
+QHBoxLayout*layout= new QHBoxLayout;
136
+/*610:*/
137
+#line 231 "./daterangeselector.w"
138
+
139
+quickSelector->addItem("Yesterday",QVariant(QStringList()<<
140
+currentDate.addDays(-1).toString(Qt::ISODate)));
141
+quickSelector->addItem("Today",QVariant(QStringList()<<
142
+currentDate.toString(Qt::ISODate)));
143
+quickSelector->insertSeparator(quickSelector->count());
144
+quickSelector->addItem("This Week",QVariant(QStringList()<<
145
+(currentDate.dayOfWeek()%7?
146
+currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate):
147
+currentDate.toString(Qt::ISODate))<<
148
+currentDate.addDays(6-(currentDate.dayOfWeek()%7)).toString(Qt::ISODate)));
149
+quickSelector->addItem("This Week to Date",currentDate.dayOfWeek()%7?
150
+QVariant(QStringList()<<
151
+currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate)<<
152
+currentDate.toString(Qt::ISODate)):
153
+QVariant(QStringList()<<currentDate.toString(Qt::ISODate)));
154
+quickSelector->addItem("Last Week",QVariant(QStringList()<<
155
+currentDate.addDays(-(currentDate.dayOfWeek()%7)-7).toString(Qt::ISODate)<<
156
+currentDate.addDays(-(currentDate.dayOfWeek()%7)-1).toString(Qt::ISODate)));
157
+quickSelector->addItem("Last 7 Days",QVariant(QStringList()<<
158
+currentDate.addDays(-6).toString(Qt::ISODate)<<
159
+currentDate.toString(Qt::ISODate)));
160
+quickSelector->insertSeparator(quickSelector->count());
161
+quickSelector->addItem("This Month",QVariant(QStringList()<<
162
+QDate(currentDate.year(),currentDate.month(),1).toString(Qt::ISODate)<<
163
+QDate(currentDate.year(),currentDate.month(),
164
+currentDate.daysInMonth()).toString(Qt::ISODate)));
165
+quickSelector->addItem("This Month to Date",(currentDate.day()==1?
166
+(QVariant(QStringList()<<currentDate.toString(Qt::ISODate))):
167
+(QVariant(QStringList()<<
168
+QDate(currentDate.year(),currentDate.month(),1).toString(Qt::ISODate)<<
169
+currentDate.toString(Qt::ISODate)))));
170
+quickSelector->addItem("Last Four Weeks",QVariant(QStringList()<<
171
+currentDate.addDays(-27).toString(Qt::ISODate)<<
172
+currentDate.toString(Qt::ISODate)));
173
+quickSelector->addItem("Last 30 Days",QVariant(QStringList()<<
174
+currentDate.addDays(-29).toString(Qt::ISODate)<<
175
+currentDate.toString(Qt::ISODate)));
176
+quickSelector->insertSeparator(quickSelector->count());
177
+quickSelector->addItem("This Quarter",QVariant(QStringList()<<
178
+QDate(currentDate.year(),currentDate.month()-((currentDate.month()-1)%3),1).toString(Qt::ISODate)<<
179
+(currentDate.month()> 9?
180
+QDate(currentDate.year(),12,31).toString(Qt::ISODate):
181
+QDate(currentDate.year(),currentDate.month()-((currentDate.month()-1)%3)+3,1).addDays(-1).toString(Qt::ISODate))));
182
+quickSelector->addItem("This Quarter to Date",
183
+(currentDate.day()==1&&(currentDate.month()-1)%3==0)?
184
+QVariant(QStringList()<<currentDate.toString(Qt::ISODate)):
185
+QVariant(QStringList()<<
186
+QDate(currentDate.year(),currentDate.month()-((currentDate.month()-1)%3),1).toString(Qt::ISODate)<<
187
+currentDate.toString(Qt::ISODate)));
188
+quickSelector->addItem("Last Quarter",currentDate.month()<4?
189
+QVariant(QStringList()<<
190
+QDate(currentDate.year()-1,10,1).toString(Qt::ISODate)<<
191
+QDate(currentDate.year()-1,12,31).toString(Qt::ISODate)):
192
+QVariant(QStringList()<<
193
+QDate(currentDate.year(),currentDate.month()-((currentDate.month()-1)%3)-3,1).toString(Qt::ISODate)<<
194
+QDate(currentDate.year(),currentDate.month()-((currentDate.month()-1)%3),1).addDays(-1).toString(Qt::ISODate)));
195
+quickSelector->addItem("Last 90 Days",QVariant(QStringList()<<
196
+currentDate.addDays(-89).toString(Qt::ISODate)<<
197
+currentDate.toString(Qt::ISODate)));
198
+quickSelector->insertSeparator(quickSelector->count());
199
+quickSelector->addItem("This Year",QVariant(QStringList()<<
200
+QDate(currentDate.year(),1,1).toString(Qt::ISODate)<<
201
+QDate(currentDate.year(),12,31).toString(Qt::ISODate)));
202
+quickSelector->addItem("This Year to Date",(currentDate.dayOfYear()==1)?
203
+QVariant(QStringList()<<currentDate.toString(Qt::ISODate)):
204
+QVariant(QStringList()<<QDate(currentDate.year(),1,1).toString(Qt::ISODate)<<
205
+currentDate.toString(Qt::ISODate)));
206
+quickSelector->addItem("Last Year",QVariant(QStringList()<<
207
+QDate(currentDate.year()-1,1,1).toString(Qt::ISODate)<<
208
+QDate(currentDate.year()-1,12,31).toString(Qt::ISODate)));
209
+quickSelector->addItem("Last 365 Days",QVariant(QStringList()<<
210
+currentDate.addDays(-364).toString(Qt::ISODate)<<
211
+currentDate.toString(Qt::ISODate)));
212
+quickSelector->insertSeparator(quickSelector->count());
213
+quickSelector->addItem("Lifetime");
214
+quickSelector->addItem("Custom");
215
+
216
+/*:610*/
217
+#line 212 "./daterangeselector.w"
218
+
219
+QToolButton*customButton= new QToolButton;
220
+customButton->setIcon(QIcon::fromTheme("office-calendar",
221
+QIcon(":/resources/icons/tango/scalable/apps/office-calendar.svg")));
222
+layout->addWidget(quickSelector);
223
+layout->addWidget(customButton);
224
+setLayout(layout);
225
+
226
+connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
227
+}
228
+
229
+/*:609*//*611:*/
230
+#line 314 "./daterangeselector.w"
231
+
232
+void DateRangeSelector::updateRange(int index)
233
+{
234
+if(index!=lastIndex&&index==quickSelector->count()-1)
235
+{
236
+toggleCustom();
237
+}
238
+else
239
+{
240
+lastIndex= index;
241
+emit rangeUpdated(quickSelector->itemData(quickSelector->currentIndex()));
242
+}
243
+}
244
+
245
+/*:611*//*612:*/
246
+#line 331 "./daterangeselector.w"
247
+
248
+void DateRangeSelector::popupHidden()
249
+{
250
+customRangeSelector->deleteLater();
251
+customRangeSelector= NULL;
252
+quickSelector->setCurrentIndex(lastIndex);
253
+}
254
+
255
+/*:612*//*613:*/
256
+#line 342 "./daterangeselector.w"
257
+
258
+void DateRangeSelector::setCustomRange(QVariant range)
259
+{
260
+quickSelector->setItemData(quickSelector->count()-1,range);
261
+emit rangeUpdated(range);
262
+lastIndex= quickSelector->count()-1;
263
+quickSelector->setCurrentIndex(lastIndex);
264
+}
265
+
266
+/*:613*//*614:*/
267
+#line 357 "./daterangeselector.w"
268
+
269
+void DateRangeSelector::toggleCustom()
270
+{
271
+if(!customRangeSelector){
272
+customRangeSelector= new CustomDateRangePopup(this);
273
+QPoint pos= rect().bottomLeft();
274
+QPoint pos2= rect().topLeft();
275
+pos= mapToGlobal(pos);
276
+pos2= mapToGlobal(pos2);
277
+QSize size= customRangeSelector->sizeHint();
278
+QRect screen= QApplication::desktop()->availableGeometry(pos);
279
+if(pos.x()+size.width()> screen.right()){
280
+pos.setX(screen.right()-size.width());
281
+}
282
+pos.setX(qMax(pos.x(),screen.left()));
283
+if(pos.y()+size.height()> screen.bottom()){
284
+pos.setY(pos2.y()-size.height());
285
+}else if(pos.y()<screen.top()){
286
+pos.setY(screen.top());
287
+}
288
+if(pos.y()<screen.top()){
289
+pos.setY(screen.top());
290
+}
291
+if(pos.y()+size.height()> screen.bottom()){
292
+pos.setY(screen.bottom()-size.height());
293
+}
294
+customRangeSelector->move(pos);
295
+customRangeSelector->show();
296
+connect(customRangeSelector,SIGNAL(hidingPopup()),
297
+this,SLOT(popupHidden()));
298
+}
299
+else
300
+{
301
+customRangeSelector->close();
302
+customRangeSelector->deleteLater();
303
+customRangeSelector= NULL;
304
+}
305
+}
306
+
307
+/*:614*//*615:*/
308
+#line 399 "./daterangeselector.w"
309
+
310
+QVariant DateRangeSelector::currentRange()
311
+{
312
+return quickSelector->itemData(lastIndex);
313
+}
314
+
315
+/*:615*//*616:*/
316
+#line 407 "./daterangeselector.w"
317
+
318
+void DateRangeSelector::setCurrentIndex(int index)
319
+{
320
+quickSelector->setCurrentIndex(index);
321
+}
322
+
323
+/*:616*//*617:*/
324
+#line 422 "./daterangeselector.w"
325
+
326
+void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
327
+{
328
+quickSelector->setItemData(quickSelector->count()-2,
329
+QVariant(QStringList()<<startDate<<endDate));
330
+}
331
+
332
+/*:617*//*618:*/
333
+#line 432 "./daterangeselector.w"
334
+
335
+void DateRangeSelector::removeIndex(int index)
336
+{
337
+quickSelector->removeItem(index);
338
+}
339
+
340
+/*:618*/
341
+#line 79 "./daterangeselector.w"
342
+
343
+
344
+#include "moc_daterangeselector.cpp"
345
+
346
+/*:603*/

+ 37
- 0
src/daterangeselector.h View File

1
+/*602:*/
2
+#line 30 "./daterangeselector.w"
3
+
4
+
5
+#include <QComboBox> 
6
+
7
+#ifndef TypicaDateRangeSelectorHeader
8
+#define TypicaDateRangeSelectorHeader
9
+
10
+class CustomDateRangePopup;
11
+
12
+class DateRangeSelector:public QWidget
13
+{
14
+Q_OBJECT
15
+public:
16
+DateRangeSelector(QWidget*parent= NULL);
17
+void setCustomRange(QVariant range);
18
+Q_INVOKABLE QVariant currentRange();
19
+public slots:
20
+void setCurrentIndex(int index);
21
+void setLifetimeRange(QString startDate,QString endDate);
22
+void removeIndex(int index);
23
+signals:
24
+void rangeUpdated(QVariant);
25
+private slots:
26
+void toggleCustom();
27
+void popupHidden();
28
+void updateRange(int index);
29
+private:
30
+QComboBox*quickSelector;
31
+CustomDateRangePopup*customRangeSelector;
32
+int lastIndex;
33
+};
34
+
35
+#endif
36
+
37
+/*:602*/

+ 478
- 0
src/daterangeselector.w View File

1
+@** A Widget for Selecting Date Ranges.
2
+
3
+\noindent Many of the reports in Typica operate over a range of dates. In these
4
+cases it should generally be possible to set that range to any arbitrary start
5
+or end, however there are some ranges that are commonly useful where it may be
6
+convenient to provide easy access to that range. While Qt provides a widget
7
+for selecting a single date, it does not provide a widget that allows two dates
8
+to be conveniently selected. One approach which Typica has previously taken is
9
+to simply use two |QDateEdit| widgets. This works, however validation that the
10
+range is valid must then be performed in every report that uses such an
11
+approach. Another down side to this is that changing either side of the date
12
+range is either going to result in a database query to obtain results in the
13
+new range or another button must be introduced to make setting a new range
14
+explicit. One typically wants to adjust both sides of the range at the same
15
+time and only have one trip to the database for the new data and increasing the
16
+number of controls required for each filter quickly creates a mess.
17
+
18
+The solution to this is the introduction of a new composite widget for
19
+selecting date ranges. The main widget consists of two parts. First there is a
20
+|QComboBox| which contains many common date ranges. A |QToolButton| is also
21
+provided for convenient one click access to the Custom range. Whether selected
22
+from the |QComboBox| or the |QToolButton|, selecting Custom creates a new pop
23
+up widget containing two |QCalendarWidget|s and a button to explicitly set the
24
+range. This button will not be available unless the selected ending date is not
25
+before the selected starting date.
26
+
27
+As the common use for the selected date is database operations, convenient
28
+access to the ISO 8601 string representation of these dates is provided.
29
+
30
+@(daterangeselector.h@>=
31
+
32
+#include <QComboBox>
33
+
34
+#ifndef TypicaDateRangeSelectorHeader
35
+#define TypicaDateRangeSelectorHeader
36
+
37
+class CustomDateRangePopup;
38
+
39
+class DateRangeSelector : public QWidget
40
+{
41
+	@[Q_OBJECT@]@;
42
+	public:@/
43
+		DateRangeSelector(QWidget *parent = NULL);
44
+		void setCustomRange(QVariant range);
45
+		Q_INVOKABLE QVariant currentRange();@/
46
+	@[public slots@]:@/
47
+		void setCurrentIndex(int index);
48
+		void setLifetimeRange(QString startDate, QString endDate);
49
+		void removeIndex(int index);@/
50
+	@[signals@]:@/
51
+		void rangeUpdated(QVariant);
52
+	@[private slots@]:@/
53
+		void toggleCustom();
54
+		void popupHidden();
55
+		void updateRange(int index);@/
56
+	private:@/
57
+		QComboBox *quickSelector;
58
+		CustomDateRangePopup *customRangeSelector;
59
+		int lastIndex;
60
+};
61
+
62
+#endif
63
+
64
+@ Implementation details are in a different file.
65
+
66
+@(daterangeselector.cpp@>=
67
+#include <QCalendarWidget>
68
+#include <QPushButton>
69
+#include <QBoxLayout>
70
+#include <QLabel>
71
+#include <QToolButton>
72
+#include <QApplication>
73
+#include <QDesktopWidget>
74
+
75
+#include "daterangeselector.h"
76
+
77
+@<CustomDateRangePopup declaration@>
78
+@<CustomDateRangePopup implementation@>
79
+@<DateRangeSelector implementation@>
80
+
81
+#include "moc_daterangeselector.cpp"
82
+
83
+@ The custom range pop up is represented as a separate class which is not to be
84
+instantiated except by |DateRangeSelector|.
85
+
86
+@<CustomDateRangePopup declaration@>=
87
+class CustomDateRangePopup : public QWidget
88
+{
89
+	@[Q_OBJECT@]@;
90
+	public:@/
91
+		CustomDateRangePopup(QWidget *parent = NULL);@/
92
+	@[public slots@]:@/
93
+		void applyRange();@/
94
+	@[signals@]:@/
95
+		void hidingPopup();@/
96
+	protected:@/
97
+		virtual void hideEvent(QHideEvent *event);@/
98
+	@[private slots@]:@/
99
+		void validateRange();@/
100
+	private:@/
101
+		QCalendarWidget *startDateSelector;
102
+		QCalendarWidget *endDateSelector;
103
+		QPushButton *applyButton;
104
+};
105
+
106
+@ The pop up constructor is responsible for laying out the component widgets,
107
+setting the dates selected in each calendar to match the currently selected
108
+range, and connecting the appropriate signal handlers.
109
+
110
+@<CustomDateRangePopup implementation@>=
111
+CustomDateRangePopup::CustomDateRangePopup(QWidget *parent) :
112
+	QWidget(parent, Qt::Popup), startDateSelector(new QCalendarWidget),
113
+	endDateSelector(new QCalendarWidget), applyButton(new QPushButton(tr("Apply")))
114
+{
115
+	setAttribute(Qt::WA_WindowPropagation);
116
+
117
+	QVBoxLayout *outerLayout = new QVBoxLayout;
118
+	QHBoxLayout *calendarsLayout = new QHBoxLayout;
119
+	QVBoxLayout *startDateLayout = new QVBoxLayout;
120
+	QVBoxLayout *endDateLayout = new QVBoxLayout;
121
+	QHBoxLayout *buttonLayout = new QHBoxLayout;
122
+	QLabel *startDateLabel = new QLabel(tr("From"));
123
+	QLabel *endDateLabel = new QLabel(tr("To"));
124
+	startDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
125
+	endDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
126
+	DateRangeSelector *selector = qobject_cast<DateRangeSelector *>(parent);
127
+	if(parent) {
128
+		QStringList range = selector->currentRange().toStringList();
129
+		startDateSelector->setSelectedDate(QDate::fromString(range.first(), Qt::ISODate));
130
+		endDateSelector->setSelectedDate(QDate::fromString(range.last(), Qt::ISODate));
131
+	}
132
+	connect(startDateSelector, SIGNAL(selectionChanged()), this, SLOT(validateRange()));
133
+	connect(endDateSelector, SIGNAL(selectionChanged()), this, SLOT(validateRange()));
134
+
135
+	startDateLayout->addWidget(startDateLabel);
136
+	startDateLayout->addWidget(startDateSelector);
137
+	endDateLayout->addWidget(endDateLabel);
138
+	endDateLayout->addWidget(endDateSelector);
139
+
140
+	connect(applyButton, SIGNAL(clicked()), this, SLOT(applyRange()));
141
+
142
+	buttonLayout->addStretch();
143
+	buttonLayout->addWidget(applyButton);
144
+
145
+	calendarsLayout->addLayout(startDateLayout);
146
+	calendarsLayout->addLayout(endDateLayout);
147
+	outerLayout->addLayout(calendarsLayout);
148
+	outerLayout->addLayout(buttonLayout);
149
+	setLayout(outerLayout);
150
+}
151
+
152
+@ The pop up can be hidden in two ways. Clicking anywhere outside of the widget
153
+will hide the pop up. Clicking the Apply button will also hide the pop up. In
154
+the former case, we must inform the parent widget that it is fine to destroy
155
+the pop up widget, which we do by emitting a signal. Note that clicking outside
156
+of the widget will cause the |QHideEvent| to be posted automatically.
157
+
158
+@<CustomDateRangePopup implementation@>=
159
+void CustomDateRangePopup::hideEvent(QHideEvent *)
160
+{
161
+	emit hidingPopup();
162
+}
163
+
164
+@ Clicking the Apply button requires setting the Custom date range to the
165
+currently selected range and then hiding the pop up manually.
166
+
167
+@<CustomDateRangePopup implementation@>=
168
+void CustomDateRangePopup::applyRange()
169
+{
170
+	DateRangeSelector *selector = qobject_cast<DateRangeSelector *>(parentWidget());
171
+	if(selector)
172
+	{
173
+		selector->setCustomRange(QVariant(QStringList() <<
174
+			startDateSelector->selectedDate().toString(Qt::ISODate) <<
175
+			endDateSelector->selectedDate().toString(Qt::ISODate)));
176
+	}
177
+	hide();
178
+}
179
+
180
+@ The Apply button is enabled or disabled depending on if the currently
181
+selected dates form a valid range in which the end date does not occur before
182
+the start date.
183
+
184
+@<CustomDateRangePopup implementation@>=
185
+void CustomDateRangePopup::validateRange()
186
+{
187
+	if(startDateSelector->selectedDate() > endDateSelector->selectedDate())
188
+	{
189
+		applyButton->setEnabled(false);
190
+	}
191
+	else
192
+	{
193
+		applyButton->setEnabled(true);
194
+	}
195
+}
196
+
197
+@ The |DateRangeSelector| constructor is responsible for setting up the layout
198
+of the |QComboBox| and the |QToolButton|, adding appropriate items to the
199
+|QComboBox|, and connecting the signals required to handle the pop up
200
+correctly.
201
+
202
+@<DateRangeSelector implementation@>=
203
+DateRangeSelector::DateRangeSelector(QWidget *parent) :
204
+	QWidget(parent), quickSelector(new QComboBox(this)),
205
+	customRangeSelector(NULL), lastIndex(0)
206
+{
207
+	connect(quickSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRange(int)));
208
+
209
+	QDate currentDate = QDate::currentDate();
210
+
211
+	QHBoxLayout *layout = new QHBoxLayout;
212
+	@<Set common date ranges to quick selector@>@;
213
+	QToolButton *customButton = new QToolButton;
214
+	customButton->setIcon(QIcon::fromTheme("office-calendar",
215
+		QIcon(":/resources/icons/tango/scalable/apps/office-calendar.svg")));
216
+	layout->addWidget(quickSelector);
217
+	layout->addWidget(customButton);
218
+	setLayout(layout);
219
+
220
+	connect(customButton, SIGNAL(clicked()), this, SLOT(toggleCustom()));
221
+}
222
+
223
+@ The |QComboBox| provides a mechanism for associating additional data with an
224
+item. Several possible representations were considered, but what was ultimately
225
+selected was a |QVariant| containing a |QStringList| in which the first entry
226
+in the list is the starting date of the range and the last entry in the list is
227
+the ending date of the range. Note that the list may contain only one item in
228
+cases where the range only covers a single date, however one should not assume
229
+that a range covering a single date will only have a single list entry.
230
+
231
+@<Set common date ranges to quick selector@>=
232
+quickSelector->addItem("Yesterday", QVariant(QStringList() <<
233
+	currentDate.addDays(-1).toString(Qt::ISODate)));
234
+quickSelector->addItem("Today", QVariant(QStringList() <<
235
+	currentDate.toString(Qt::ISODate)));
236
+quickSelector->insertSeparator(quickSelector->count());
237
+quickSelector->addItem("This Week", QVariant(QStringList() <<
238
+	(currentDate.dayOfWeek() % 7 ?
239
+		currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate) :
240
+		currentDate.toString(Qt::ISODate)) <<
241
+			currentDate.addDays(6 - (currentDate.dayOfWeek() % 7)).toString(Qt::ISODate)));
242
+quickSelector->addItem("This Week to Date", currentDate.dayOfWeek() % 7 ?
243
+	QVariant(QStringList() <<
244
+	currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate) <<
245
+	currentDate.toString(Qt::ISODate)) :
246
+	QVariant(QStringList() << currentDate.toString(Qt::ISODate)));
247
+quickSelector->addItem("Last Week", QVariant(QStringList() <<
248
+	currentDate.addDays(-(currentDate.dayOfWeek() % 7) - 7).toString(Qt::ISODate) <<
249
+	currentDate.addDays(-(currentDate.dayOfWeek() % 7) - 1).toString(Qt::ISODate)));
250
+quickSelector->addItem("Last 7 Days", QVariant(QStringList() <<
251
+	currentDate.addDays(-6).toString(Qt::ISODate) <<
252
+	currentDate.toString(Qt::ISODate)));
253
+quickSelector->insertSeparator(quickSelector->count());
254
+quickSelector->addItem("This Month", QVariant(QStringList() <<
255
+	QDate(currentDate.year(), currentDate.month(), 1).toString(Qt::ISODate) <<
256
+	QDate(currentDate.year(), currentDate.month(),
257
+		currentDate.daysInMonth()).toString(Qt::ISODate)));
258
+quickSelector->addItem("This Month to Date", (currentDate.day() == 1 ?
259
+	(QVariant(QStringList() << currentDate.toString(Qt::ISODate))) :
260
+	(QVariant(QStringList() <<
261
+	QDate(currentDate.year(), currentDate.month(), 1).toString(Qt::ISODate) <<
262
+	currentDate.toString(Qt::ISODate)))));
263
+quickSelector->addItem("Last Four Weeks", QVariant(QStringList() <<
264
+	currentDate.addDays(-27).toString(Qt::ISODate) <<
265
+	currentDate.toString(Qt::ISODate)));
266
+quickSelector->addItem("Last 30 Days", QVariant(QStringList() <<
267
+	currentDate.addDays(-29).toString(Qt::ISODate) <<
268
+	currentDate.toString(Qt::ISODate)));
269
+quickSelector->insertSeparator(quickSelector->count());
270
+quickSelector->addItem("This Quarter", QVariant(QStringList() <<
271
+	QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).toString(Qt::ISODate) <<
272
+	(currentDate.month() > 9 ?
273
+		QDate(currentDate.year(), 12, 31).toString(Qt::ISODate) :
274
+		QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3) + 3, 1).addDays(-1).toString(Qt::ISODate))));
275
+quickSelector->addItem("This Quarter to Date",
276
+	(currentDate.day() == 1 && (currentDate.month() - 1) % 3 == 0) ?
277
+		QVariant(QStringList() << currentDate.toString(Qt::ISODate)) :
278
+		QVariant(QStringList() <<
279
+		QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).toString(Qt::ISODate) <<
280
+		currentDate.toString(Qt::ISODate)));
281
+quickSelector->addItem("Last Quarter", currentDate.month() < 4 ?
282
+	QVariant(QStringList() <<
283
+		QDate(currentDate.year() - 1, 10, 1).toString(Qt::ISODate) <<
284
+		QDate(currentDate.year() - 1, 12, 31).toString(Qt::ISODate)) :
285
+	QVariant(QStringList() <<
286
+		QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3) - 3, 1).toString(Qt::ISODate) <<
287
+		QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).addDays(-1).toString(Qt::ISODate)));
288
+quickSelector->addItem("Last 90 Days", QVariant(QStringList() <<
289
+	currentDate.addDays(-89).toString(Qt::ISODate) <<
290
+	currentDate.toString(Qt::ISODate)));
291
+quickSelector->insertSeparator(quickSelector->count());
292
+quickSelector->addItem("This Year", QVariant(QStringList() <<
293
+	QDate(currentDate.year(), 1, 1).toString(Qt::ISODate) <<
294
+	QDate(currentDate.year(), 12, 31).toString(Qt::ISODate)));
295
+quickSelector->addItem("This Year to Date", (currentDate.dayOfYear() == 1) ?
296
+	QVariant(QStringList() << currentDate.toString(Qt::ISODate)) :
297
+	QVariant(QStringList() << QDate(currentDate.year(), 1, 1).toString(Qt::ISODate) <<
298
+	currentDate.toString(Qt::ISODate)));
299
+quickSelector->addItem("Last Year", QVariant(QStringList() <<
300
+	QDate(currentDate.year() - 1, 1, 1).toString(Qt::ISODate) <<
301
+	QDate(currentDate.year() - 1, 12, 31).toString(Qt::ISODate)));
302
+quickSelector->addItem("Last 365 Days", QVariant(QStringList() <<
303
+	currentDate.addDays(-364).toString(Qt::ISODate) <<
304
+	currentDate.toString(Qt::ISODate)));
305
+quickSelector->insertSeparator(quickSelector->count());
306
+quickSelector->addItem("Lifetime");
307
+quickSelector->addItem("Custom");
308
+
309
+@ Special handling of the Custom range is required because it is possible to
310
+select this from the |QComboBox| and then not set a range. This should result
311
+in the selection changing back to the most recent valid selection. Creating the
312
+pop up in this way is handled in |updateRange()|.
313
+
314
+@<DateRangeSelector implementation@>=
315
+void DateRangeSelector::updateRange(int index)
316
+{
317
+	if(index != lastIndex && index == quickSelector->count() - 1)
318
+	{
319
+		toggleCustom();
320
+	}
321
+	else
322
+	{
323
+		lastIndex = index;
324
+		emit rangeUpdated(quickSelector->itemData(quickSelector->currentIndex()));
325
+	}
326
+}
327
+
328
+@ Resetting the range to the most recent valid selection is handled in
329
+|popupHidden()|.
330
+
331
+@<DateRangeSelector implementation@>=
332
+void DateRangeSelector::popupHidden()
333
+{
334
+	customRangeSelector->deleteLater();
335
+	customRangeSelector = NULL;
336
+	quickSelector->setCurrentIndex(lastIndex);
337
+}
338
+
339
+@ If Custom is set to a new valid range, |lastIndex| will have been set to
340
+point to the appropriate item by a call to |setCustomRange()|.
341
+
342
+@<DateRangeSelector implementation@>=
343
+void DateRangeSelector::setCustomRange(QVariant range)
344
+{
345
+	quickSelector->setItemData(quickSelector->count() - 1, range);
346
+	emit rangeUpdated(range);
347
+	lastIndex = quickSelector->count() - 1;
348
+	quickSelector->setCurrentIndex(lastIndex);
349
+}
350
+
351
+@ When creating the pop up, it should ideally be placed such that the left of
352
+the pop up is aligned with the left of the widget that is normally shown and
353
+immediately under it, however if this would result in part of the pop up not
354
+fitting on the same screen, it should be moved to make a best effort at full
355
+visibility.
356
+
357
+@<DateRangeSelector implementation@>=
358
+void DateRangeSelector::toggleCustom()
359
+{
360
+	if(!customRangeSelector) {
361
+		customRangeSelector = new CustomDateRangePopup(this);
362
+		QPoint pos = rect().bottomLeft();
363
+		QPoint pos2 = rect().topLeft();
364
+		pos = mapToGlobal(pos);
365
+		pos2 = mapToGlobal(pos2);
366
+		QSize size = customRangeSelector->sizeHint();
367
+		QRect screen = QApplication::desktop()->availableGeometry(pos);
368
+		if(pos.x()+size.width() > screen.right()) {
369
+			pos.setX(screen.right()-size.width());
370
+		}
371
+		pos.setX(qMax(pos.x(), screen.left()));
372
+		if(pos.y() + size.height() > screen.bottom()) {
373
+			pos.setY(pos2.y() - size.height());
374
+		} else if (pos.y() < screen.top()){
375
+			pos.setY(screen.top());
376
+		}
377
+		if(pos.y() < screen.top()) {
378
+			pos.setY(screen.top());
379
+		}
380
+		if(pos.y()+size.height() > screen.bottom()) {
381
+			pos.setY(screen.bottom()-size.height());
382
+		}
383
+		customRangeSelector->move(pos);
384
+		customRangeSelector->show();
385
+		connect(customRangeSelector, SIGNAL(hidingPopup()),
386
+				this, SLOT(popupHidden()));
387
+    }
388
+	else
389
+	{
390
+		customRangeSelector->close();
391
+		customRangeSelector->deleteLater();
392
+		customRangeSelector = NULL;
393
+	}
394
+}
395
+
396
+@ While a signal is emitted when the selected range changes, it is frequently
397
+convenient to have a way to request the currently selected range at any time.
398
+
399
+@<DateRangeSelector implementation@>=
400
+QVariant DateRangeSelector::currentRange()
401
+{
402
+	return quickSelector->itemData(lastIndex);
403
+}
404
+
405
+@ Similarly, a method is provided to set the current index of the combo box.
406
+
407
+@<DateRangeSelector implementation@>=
408
+void DateRangeSelector::setCurrentIndex(int index)
409
+{
410
+	quickSelector->setCurrentIndex(index);
411
+}
412
+
413
+@ The Lifetime range is handled somewhat differently from other ranges as there
414
+is no general way to know what that range should be without making unsafe
415
+assumptions. As such, reports are expected to remove the option, provide a
416
+sensible range for it, or handle this selection in a special case. The expected
417
+source of the lifetime date range is the result of a database query so a method
418
+is provided that accepts string representations of the dates. Note that this
419
+method must not be called if the Lifetime option is no longer the second to
420
+last option in the combo box.
421
+
422
+@<DateRangeSelector implementation@>=
423
+void DateRangeSelector::setLifetimeRange(QString startDate, QString endDate)
424
+{
425
+	quickSelector->setItemData(quickSelector->count() - 2,
426
+		QVariant(QStringList() << startDate << endDate));
427
+}
428
+
429
+@ The |removeIndex()| method is intended for removing the Lifetime option in
430
+cases where this is not supported. Use of this method is strongly discouraged.
431
+
432
+@<DateRangeSelector implementation@>=
433
+void DateRangeSelector::removeIndex(int index)
434
+{
435
+	quickSelector->removeItem(index);
436
+}
437
+
438
+@ To use this new control in Typica, we should provide a way to create it from
439
+the XML description of a window.
440
+
441
+@<Additional box layout elements@>=
442
+else if(currentElement.tagName() == "daterange")
443
+{
444
+	addDateRangeToLayout(currentElement, widgetStack, layoutStack);
445
+}
446
+
447
+@ The method for adding a date range selector to a layout is currently trivial.
448
+The |"id"| attribute is supported as usual, as is an |"initial"| attribute for
449
+setting the combo box index.
450
+
451
+@<Functions for scripting@>=
452
+void addDateRangeToLayout(QDomElement element, QStack<QWidget *> *,@|
453
+                          QStack<QLayout *> *layoutStack)
454
+{
455
+	DateRangeSelector *widget = new DateRangeSelector;
456
+	if(element.hasAttribute("id"))
457
+	{
458
+		widget->setObjectName(element.attribute("id"));
459
+	}
460
+	if(element.hasAttribute("initial"))
461
+	{
462
+		widget->setCurrentIndex(element.attribute("initial").toInt());
463
+	}
464
+	QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
465
+	layout->addWidget(widget);
466
+}
467
+
468
+@ The prototype needs to be specified.
469
+
470
+@<Function prototypes for scripting@>=
471
+void addDateRangeToLayout(QDomElement element,
472
+                          QStack<QWidget *> *widgetStack,
473
+                          QStack<QLayout *> *layoutStack);
474
+
475
+@ Our header is also required.
476
+
477
+@<Header files to include@>=
478
+#include "daterangeselector.h"

+ 2
- 2
src/draglabel.cpp View File

1
-/*842:*/
1
+/*863:*/
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
-/*:842*/
29
+/*:863*/

+ 2
- 2
src/draglabel.h View File

1
-/*841:*/
1
+/*862:*/
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
-/*:841*/
20
+/*:862*/

+ 19
- 38
src/rate.w View File

29
 		int ct;
29
 		int ct;
30
 		int st;
30
 		int st;
31
 		QList<Measurement> cache;
31
 		QList<Measurement> cache;
32
-		QMap<double,double> smoothCache;
33
 };
32
 };
34
 
33
 
35
 @ The interesting part of this class is in the |newMeasurement()| method. This
34
 @ The interesting part of this class is in the |newMeasurement()| method. This
88
 	}
87
 	}
89
 }
88
 }
90
 
89
 
91
-@ The calculation method here is subject to change as this is still noisier
92
-than I would like. What we are doing here is calculating the rate of change
93
-between each pair of adjacent measurements in the cache and averaging them to
94
-produce something that is a little less noisy than just using the first and
95
-last measurements in the cache. Other techniques may be useful for reducing the
96
-noise further.
90
+@ Rather than work directly with changes from one measurement to the next and
91
+attempting to filter out the noise, we instead calculate the slope of a simple
92
+linear regression on the current window of data.
97
 
93
 
98
 The measurement will carry the fact that it is a relative measurement.
94
 The measurement will carry the fact that it is a relative measurement.
99
 
95
 
100
 @<Calculate rate of change@>=
96
 @<Calculate rate of change@>=
101
-QList<double> rates;
102
-for(int i = 1; i < cache.size(); i++)
97
+int N = cache.size();
98
+double SXY = 0;
99
+double SX = 0;
100
+double SXX = 0;
101
+double SY = 0;
102
+double y;
103
+double x;
104
+for(int i = 0; i < N; i++)
103
 {
105
 {
104
-	double mdiff = cache.at(i).temperature() - cache.at(i-1).temperature();
105
-	double tdiff = (double)(cache.at(i-1).time().msecsTo(cache.at(i).time())) / 1000.0;
106
-	rates.append(mdiff/tdiff);
106
+	y = cache.at(i).temperature();
107
+	SY += y;
108
+	x = cache.at(0).time().msecsTo(cache.at(i).time()) / 1000.0;
109
+	SX += x;
110
+	SXX += (x*x);
111
+	SXY += (x*y);
107
 }
112
 }
108
-double acc = 0.0;
109
-for(int i = 0; i < rates.size(); i++)
110
-{
111
-	acc += rates.at(i);
112
-}
113
-double pavg = acc /= rates.size();
114
-double v2 = pavg * st;
115
-double refm = cache.back().temperature() - cache.front().temperature();
116
-double reft = (double)(cache.front().time().msecsTo(cache.back().time())) / 1000.0;
117
-double ref = refm/reft;
118
-Measurement value(v2, cache.back().time(), cache.back().scale());
113
+double M = ((N * SXY) - (SX * SY)) / ((N * SXX) - (SX * SX));
114
+Measurement value(M * st, cache.back().time(), cache.back().scale());
119
 value.insert("relative", true);
115
 value.insert("relative", true);
120
 emit newData(value);
116
 emit newData(value);
121
-double calcdiff = ref - pavg;
122
-if(calcdiff < 0)
123
-{
124
-	calcdiff = -calcdiff;
125
-}
126
-if(pavg < 0)
127
-{
128
-	pavg = -pavg;
129
-}
130
-if(calcdiff > (pavg * 0.2))
131
-{
132
-	Measurement save = cache.back();
133
-	cache.clear();
134
-	cache.append(save);
135
-}
136
 
117
 
137
 @ The rest of the class implementation is trivial.
118
 @ The rest of the class implementation is trivial.
138
 
119
 

+ 4
- 0
src/resources.qrc View File

23
         <file>resources/icons/tango/22x22/categories/applications-graphics.png</file>
23
         <file>resources/icons/tango/22x22/categories/applications-graphics.png</file>
24
         <file>resources/icons/tango/32x32/categories/applications-graphics.png</file>
24
         <file>resources/icons/tango/32x32/categories/applications-graphics.png</file>
25
         <file>resources/icons/tango/scalable/categories/applications-graphics.svg</file>
25
         <file>resources/icons/tango/scalable/categories/applications-graphics.svg</file>
26
+        <file>resources/icons/tango/16x16/apps/office-calendar.png</file>
27
+        <file>resources/icons/tango/22x22/apps/office-calendar.png</file>
28
+        <file>resources/icons/tango/32x32/apps/office-calendar.png</file>
29
+        <file>resources/icons/tango/scalable/apps/office-calendar.svg</file>
26
     </qresource>
30
     </qresource>
27
 </RCC>
31
 </RCC>

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

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.1</string>
10
+	<string>Typica 1.6.2</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.1</string>
20
+	<string>1.6.2</string>
21
 	<key>CFBundleVersion</key>
21
 	<key>CFBundleVersion</key>
22
-	<string>1.6.1</string>
22
+	<string>1.6.2</string>
23
 	<key>NSHumanReadableCopyright</key>
23
 	<key>NSHumanReadableCopyright</key>
24
-	<string>© 2007–2013 Neal Wilson</string>
24
+	<string>© 2007–2014 Neal Wilson</string>
25
 </dict>
25
 </dict>
26
 </plist>
26
 </plist>

+ 2
- 3
src/resources/html/about.html View File

10
 				<div id="topbanner">
10
 				<div id="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.1</h2>
13
+					<h2>Version 1.6.2</h2>
14
 				</div>
14
 				</div>
15
 			<div id="maintext">
15
 			<div id="maintext">
16
-				<p>Copyright &copy; 2007&ndash;2013 Neal Evan Wilson
16
+				<p>Copyright &copy; 2007&ndash;2014 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>
26
 				<p>Ongoing development of Typica is made possible through the
26
 				<p>Ongoing development of Typica is made possible through the
27
 				generous financial contributions from the following:</p>
27
 				generous financial contributions from the following:</p>
28
 
28
 
29
-				<p>Pinnacle Coffee Roasting</p>
30
 				<p>Anonymous Funders Through
29
 				<p>Anonymous Funders Through
31
 				<a href="https://www.gittip.com/N3Roaster/">Gittip</a></p>
30
 				<a href="https://www.gittip.com/N3Roaster/">Gittip</a></p>
32
 
31
 

+ 6
- 6
src/scale.cpp View File

1
-/*848:*/
1
+/*869:*/
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
-/*:848*//*849:*/
13
+/*:869*//*870:*/
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
-/*850:*/
27
+/*871:*/
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
-/*:850*/
56
+/*:871*/
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
-/*:849*//*851:*/
64
+/*:870*//*872:*/
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
-/*:851*/
77
+/*:872*/

+ 2
- 2
src/scale.h View File

1
-/*847:*/
1
+/*868:*/
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
-/*:847*/
28
+/*:868*/

+ 1297
- 1269
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,1,0
4
-PRODUCTVERSION 	1,6,1,0
3
+FILEVERSION 	1,6,2,0
4
+PRODUCTVERSION 	1,6,2,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.1\0"
20
-			VALUE "FileVersion", "1.6.1\0"
19
+			VALUE "FileDescription", "Typica 1.6.2\0"
20
+			VALUE "FileVersion", "1.6.2\0"
21
 			VALUE "InternalName", "Typica\0"
21
 			VALUE "InternalName", "Typica\0"
22
-			VALUE "LegalCopyright", "Copyright 2007-2013 Neal Evan Wilson\0"
22
+			VALUE "LegalCopyright", "Copyright 2007-2014 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.1\0"
25
+			VALUE "ProductVersion", "1.6.2\0"
26
 		END
26
 		END
27
 	END
27
 	END
28
 	BLOCK "VarFileInfo"
28
 	BLOCK "VarFileInfo"

+ 5
- 2
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.1 \number\year-\number\month-\number\day}
26
-\def\years{2007--2013}
25
+\def\version{1.6.2 \number\year-\number\month-\number\day}
26
+\def\years{2007--2014}
27
 \def\title{\pn{} (Version \version)}
27
 \def\title{\pn{} (Version \version)}
28
 \newskip\dangerskipb
28
 \newskip\dangerskipb
29
 \newskip\dangerskip
29
 \newskip\dangerskip
4263
 				QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
4263
 				QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
4264
 				layout->addStretch();
4264
 				layout->addStretch();
4265
 			}
4265
 			}
4266
+			@<Additional box layout elements@>@;
4266
 		}
4267
 		}
4267
 	}
4268
 	}
4268
 }
4269
 }
13268
 	value.setProperty("print", engine->newFunction(QTextEdit_print));
13269
 	value.setProperty("print", engine->newFunction(QTextEdit_print));
13269
 }
13270
 }
13270
 
13271
 
13272
+@i daterangeselector.w
13273
+
13271
 @** An area for repeated user interface elements.
13274
 @** An area for repeated user interface elements.
13272
 
13275
 
13273
 \noindent There are multiple use cases in which it is useful to specify a
13276
 \noindent There are multiple use cases in which it is useful to specify a

+ 5
- 5
src/units.w View File

386
 @ The implementation of these functions is trivial.
386
 @ The implementation of these functions is trivial.
387
 
387
 
388
 @<Functions for scripting@>=
388
 @<Functions for scripting@>=
389
-QScriptValue Units_convertTemperature(QScriptContext *context, QScriptEngine *engine)
389
+QScriptValue Units_convertTemperature(QScriptContext *context, QScriptEngine *)
390
 {
390
 {
391
 	return QScriptValue(Units::convertTemperature(argument<double>(0, context),
391
 	return QScriptValue(Units::convertTemperature(argument<double>(0, context),
392
 	                                              argument<Units::Unit>(1, context),
392
 	                                              argument<Units::Unit>(1, context),
394
 }
394
 }
395
 
395
 
396
 QScriptValue Units_convertRelativeTemperature(QScriptContext *context,
396
 QScriptValue Units_convertRelativeTemperature(QScriptContext *context,
397
-                                              QScriptEngine *engine)
397
+                                              QScriptEngine *)
398
 {
398
 {
399
 	return QScriptValue(Units::convertRelativeTemperature(
399
 	return QScriptValue(Units::convertRelativeTemperature(
400
 	                         argument<double>(0, context),
400
 	                         argument<double>(0, context),
402
 	                         argument<Units::Unit>(2, context)));
402
 	                         argument<Units::Unit>(2, context)));
403
 }
403
 }
404
 
404
 
405
-QScriptValue Units_isTemperatureUnit(QScriptContext *context, QScriptEngine *engine)
405
+QScriptValue Units_isTemperatureUnit(QScriptContext *context, QScriptEngine *)
406
 {
406
 {
407
 	return QScriptValue(Units::isTemperatureUnit(argument<Units::Unit>(0, context)));
407
 	return QScriptValue(Units::isTemperatureUnit(argument<Units::Unit>(0, context)));
408
 }
408
 }
409
 
409
 
410
-QScriptValue Units_convertWeight(QScriptContext *context, QScriptEngine *engine)
410
+QScriptValue Units_convertWeight(QScriptContext *context, QScriptEngine *)
411
 {
411
 {
412
 	return QScriptValue(Units::convertWeight(argument<double>(0, context),
412
 	return QScriptValue(Units::convertWeight(argument<double>(0, context),
413
 	                                         argument<Units::Unit>(1, context),
413
 	                                         argument<Units::Unit>(1, context),
414
 	                                         argument<Units::Unit>(2, context)));
414
 	                                         argument<Units::Unit>(2, context)));
415
 }
415
 }
416
 
416
 
417
-QScriptValue Units_isWeightUnit(QScriptContext *context, QScriptEngine *engine)
417
+QScriptValue Units_isWeightUnit(QScriptContext *context, QScriptEngine *)
418
 {
418
 {
419
 	return QScriptValue(Units::isWeightUnit(argument<Units::Unit>(0, context)));
419
 	return QScriptValue(Units::isWeightUnit(argument<Units::Unit>(0, context)));
420
 }
420
 }

+ 1
- 1
src/valueannotation.w View File

276
 @ The implementation of the functions also proceeds as usual.
276
 @ The implementation of the functions also proceeds as usual.
277
 
277
 
278
 @<Functions for scripting@>=
278
 @<Functions for scripting@>=
279
-QScriptValue constructValueAnnotation(QScriptContext *context, QScriptEngine *engine)
279
+QScriptValue constructValueAnnotation(QScriptContext *, QScriptEngine *engine)
280
 {
280
 {
281
 	QScriptValue object = engine->newQObject(new ValueAnnotation);
281
 	QScriptValue object = engine->newQObject(new ValueAnnotation);
282
 	setValueAnnotationProperties(object, engine);
282
 	setValueAnnotationProperties(object, engine);

Loading…
Cancel
Save