Added get_order_ids_by_state().
[readifood.git] / lib / order.php
1 <?php
2
3   if (isset($_POST['show_add_order'])) {
4     $area_id = $_POST['area_id'];
5     show_new_order_form($area_id);
6   }
7   else if (isset($_POST['add_order'])) {
8     $id = add_order();
9     if ($id !== false) {
10       echo "<p>Order placed.</p>\n";
11       $parameters = array("id", $id);
12     }
13   }
14   else if (isset($_POST['update_order'])) {
15     list($ignored, $id, $args) = parse_parameters($parameters);
16     $q = new OrderQuery;
17     $order = $q->findOneById($id);
18     if ($order) {
19       if (update_order($order) !== false) {
20         echo "<p>Updated order.</p>\n";
21         $parameters = array("id", $order->getId());
22       }
23     }
24     else {
25       echo "<p>No such contact!</p>\n";
26     }
27   }
28   else if ($_POST['show_in_area']) {
29     $q = new AreaQuery;
30     $area = $q->findOneById($_POST['area_id']);
31     header(sprintf("Location: http%s://%s/%s/in/area/%s/%d%s", ($_SERVER['HTTPS']) ? "s" : "", $_SERVER['HTTP_HOST'], $module, urlencode($area->getName()), $_POST['area_id'], get_order_state_uri(get_order_state_mask())));
32     exit;
33   }
34   else if ($_POST['show_in_city']) {
35     $q = new CityQuery;
36     $city = $q->findOneById($_POST['city_id']);
37     header(sprintf("Location: http%s://%s/%s/in/city/%s/%d%s", ($_SERVER['HTTPS']) ? "s" : "", $_SERVER['HTTP_HOST'], $module, urlencode($city->getName()), $_POST['city_id'], get_order_state_uri(get_order_state_mask())));
38     exit;
39   }
40
41   function show_orders($offset, $per_page, $requester_ids = null, $beneficiary_ids = null, $state_mask = null) {
42     /* XXX: Use Propel methods. */
43     if (isset($state_mask)) $order_ids = get_order_ids_by_state($state_mask);
44     $q = new OrderQuery;
45     if (isset($requester_ids)) $q->filterByRequesterId($requester_ids);
46     if (isset($beneficiary_ids)) $q->filterByBeneficiaryId($beneficiary_ids);
47     # XXX: Doesn't work.
48     #if (isset($state_mask)) $q->useOrderStateQuery()->addSelectQuery($latest_state, 'latestState')->where("order_id=latestState.order_id")->where("state & $state_mask")->endUse();
49     if (isset($state_mask)) $q->filterById($order_ids);
50     $orders = $q->orderByDate()->find();
51     if (count($orders)) {
52       foreach ($orders as $order) {
53         echo "<br>\nOrder " . $order->getStrongLink($order->getId()) . ": " . get_order_displayname($order);
54         if (check_admin(1)) {
55           echo " " . $order->getDeleteLink();
56         }
57
58         /* XXX: Should pull from query. */
59         $q = new ContactQuery;
60         $contact = $q->findOneById($order->getBeneficiaryId());
61         if ($contact) {
62           echo " for " . $contact->getLink();
63           $area = get_contact_area($contact);
64           if ($area) echo " in " . $area->getLink();
65         }
66
67         if ($order->getHubId()) {
68           $q = new HubQuery;
69           $hub = $q->findOneById($order->getHubId());
70           if ($hub) echo " to hub " . $hub->getLink();
71           $area = get_hub_area($hub);
72           if ($area) echo " in " . $area->getLink();
73         }
74       }
75     }
76     else echo " none";
77   }
78
79   function show_city_orders($offset, $per_page, $city_name, $city_id = null, $state_mask = null) {
80     if (isset($city_id)) $city = get_city_by_id($city_id);
81     else if ($city_name) $city = get_city_by_name($city_name);
82     if ($city) {
83       $contacts = get_city_contacts($city->getId(), $GLOBALS['ROLE_BENEFICIARY']);
84       $beneficiary_ids = array();
85       foreach ($contacts as $contact) $beneficiary_ids[] = $contact->getId();
86
87       echo "<p>Orders in city " . $city->getLink(get_city_displayname($city)) . ":";
88       return show_orders($offset, $per_page, null, $beneficiary_ids, $state_mask);
89     }
90     else echo "<p>No such city!</p>\n";
91   }
92
93   function show_requester_orders($offset, $per_page, $contact_name, $contact_id = null, $state_mask = null) {
94     if (isset($contact_id)) $contact = get_contact_by_id($contact_id);
95     else if ($contact_name) $contact = get_contact_by_name($contact_name);
96     if ($contact) {
97       echo "<p>Orders from referrer " . $contact->getLink() . ":";
98       return show_orders($offset, $per_page, $contact->getId(), null, $state_mask);
99     }
100     else echo "<p>No such contact!</p>\n";
101   }
102
103   function show_beneficiary_orders($offset, $per_page, $contact_name, $contact_id = null, $state_mask = null) {
104     if (isset($contact_id)) $contact = get_contact_by_id($contact_id);
105     else if ($contact_name) $contact = get_contact_by_name($contact_name);
106     if ($contact) {
107       echo "<p>Orders to beneficiary " . $contact->getLink() . ":";
108       return show_orders($offset, $per_page, null, $contact->getId(), $state_mask);
109     }
110     else echo "<p>No such contact!</p>\n";
111   }
112
113   function show_area_orders($offset, $per_page, $area_name, $area_id = null, $state_mask = null) {
114     if (isset($area_id)) $area = get_area_by_id($area_id);
115     else if ($area_name) $area = get_area_by_name($area_name);
116     if ($area) {
117       $contacts = get_area_contacts($area->getId(), $GLOBALS['ROLE_BENEFICIARY']);
118       $contact_ids = array();
119       foreach ($contacts as $contact) $contact_ids[] = $contact->getId();
120
121       echo "<p>Orders in area " . $area->getLink() . ":";
122       return show_orders($offset, $per_page, null, $contact_ids, $state_mask);
123     }
124     else echo "<p>No such area!</p>\n";
125   }
126
127   function show_order_state_form($state_mask = null) {
128     global $states, $all_states;
129
130     if (is_null($state_mask)) $state_mask = $all_states;
131
132     echo "<p>Restrict to order states:\n";
133     for ($i = 0; $i < count($states); $i++) {
134       echo " <input type=\"checkbox\" name=\"state_$i\"";
135       if ($state_mask & (1 << $i)) echo " checked";
136       echo ">$states[$i]\n";
137     }
138     echo "</p>\n";
139   }
140
141   function get_order_state_mask($string = null) {
142     global $states, $all_states;
143
144     $mask = 0;
145
146     if (isset($string)) {
147       $selected = explode("+", $string);
148       for ($i = 0; $i < count($states); $i++) {
149         if (in_array($states[$i], $selected)) $mask |= (1 << $i);
150       }
151     }
152     else {
153       for ($i = 0; $i < count($states); $i++) {
154         if ($_POST['state_' . $i] == "on") $mask |= (1 << $i);
155       }
156     }
157
158     if (! $mask) $mask = $all_states;
159     return $mask;
160   }
161
162   function get_order_state_string($mask) {
163     global $states;
164
165     $selected = array();
166
167     for ($i = 0; $i < count($states); $i++) {
168       if ($mask & (1 << $i)) $selected[] = $states[$i];
169     }
170
171     return implode("+", $selected);
172   }
173
174   function get_order_state_uri($mask) {
175     global $all_states;
176
177     if (is_null($mask)) return "";
178     if ($mask == $all_states) return "";
179
180     return "/state/" . get_order_state_string($mask);
181   }
182
183   function show_order_areas_form($city_id = null) {
184     $areas = get_city_areas($city_id);
185     if (! count($areas)) {
186       echo "<p>No <a href=\"/area\">areas</a>!</p>\n";
187       return;
188     }
189
190     echo "<p>Show orders in area\n";
191     echo "<select name=\"area_id\">\n";
192     foreach ($areas as $area) {
193       option("area_id", $area->getId(), get_area_displayname($area));
194     }
195     echo "</select>\n";
196     echo "<input type=\"submit\" name=\"show_in_area\" value=\"Show\">\n";
197   }
198
199   function show_order_cities_form($city_id = null) {
200     $q = new CityQuery;
201     $cities = $q->orderByName()->find();
202
203     if (! count($cities)) {
204       echo "<p>No <a href=\"/city\">cities</a>!</p>\n";
205       return;
206     }
207
208     echo "<p>Show orders in city\n";
209     echo "<select name=\"city_id\">\n";
210     foreach ($cities as $city) {
211       option("city_id", $city->getId(), get_city_displayname($city), $city_id);
212     }
213     echo "</select>\n";
214     echo "<input type=\"submit\" name=\"show_in_city\" value=\"Show\">\n";
215   }
216
217   function show_order_forms($city_id, $state_mask) {
218     echo "<form method=\"POST\" action=\"" . $_SERVER['REQUEST_URI'] . "\">\n";
219     show_order_state_form($state_mask);
220     show_order_areas_form($city_id);
221     show_order_cities_form($city_id);
222     echo "</form>\n";
223   }
224
225   function show_order_form($order = null, $area_id = null) {
226     global $states, $parcel_sizes, $parcel_contents;
227
228     if ($order) {
229       $q = new OrderStateQuery;
230       $order_state = $q->filterByOrderId($order->getId())->orderByUpdated('desc')->limit(1)->findOne();
231       if ($order_state) {
232         $state = $order_state->getState();
233         $driver_id = $order_state->getDriverId();
234       }
235     }
236     else $order = new Order;
237
238
239     /* Date. */
240     echo "<tr>\n";
241     echo "  <td>Delivery</td>\n";
242     /* XXX: Find suitable dates from area. */
243     echo "  <td>";
244     show_date_form("date", $order->getDate());
245     if (! $order->getDate()) {
246       echo " and recur for <select name=\"recurrence\">\n";
247       for ($i = 0; $i < 4; $i++) option("recurrence", $i, $i);
248       echo "</select> weeks";
249     }
250     echo "</td>\n";
251     echo "</tr>\n";
252
253     /* Referrer. */
254     echo "<tr>\n";
255     echo "  <td>Referrer</td>\n";
256     echo "  <td><select name=\"requester_id\">\n";
257     option("requester_id", null, "");
258     $contacts = get_area_requesters();
259     foreach ($contacts as $contact) {
260       option("requester_id", $contact->getId(), $contact->getDisplayname(), $order->getRequesterId());
261     }
262     echo "</select></td>\n";
263     echo "</tr>\n";
264
265     /* Beneficiary. */
266     echo "<tr>\n";
267     echo "  <td>Beneficiary</td>\n";
268     echo "  <td><select name=\"beneficiary_id\">\n";
269     option("beneficiary_id", null, "");
270     if (! $order->getId() && $order->getBeneficiaryId()) {
271       $contact = get_contact_by_id($order->getBeneficiaryId());
272       if ($contact) option("beneficiary_id", $order->getBeneficiaryId(), $contact->getDisplayname(), $order->getBeneficiaryId());
273     }
274     else {
275       $contacts = get_area_beneficiaries($area_id);
276       foreach ($contacts as $contact) {
277         option("beneficiary_id", $contact->getId(), $contact->getDisplayname(), $order->getBeneficiaryId());
278       }
279     }
280     echo "</select></td>\n";
281     echo "</tr>\n";
282
283     /* Hub. */
284     echo "<tr>\n";
285     echo "  <td>Hub</td>\n";
286     echo "  <td><select name=\"hub_id\">\n";
287     option("hub_id", null, "");
288     $hubs = get_area_hubs();
289     foreach ($hubs as $hub) {
290       option("hub_id", $hub->getId(), $hub->getDisplayname(), $order->getHubId());
291     }
292     echo "</select></td>\n";
293     echo "</tr>\n";
294
295     /* Parcel type. */
296     echo "<tr>\n";
297     echo "  <td>Parcel size</td>\n";
298     echo "  <td><select name=\"parcel_size\">\n";
299     $mask = 1 << count($parcel_sizes);
300     for ($i = 0; $i < count($parcel_sizes); $i++) {
301       option("parcel_size", 1 << $i, $parcel_sizes[$i], $order->getParcel() % $mask);
302     }
303     echo "</select></td>\n";
304     echo "</tr>\n";
305
306     /* Parcel contents. */
307     echo "<tr>\n";
308     echo "  <td>Parcel contents</td>\n";
309     echo "  <td>";
310     for ($i = count($parcel_sizes); $i < count($parcel_contents); $i++) {
311       echo "  <input type=\"checkbox\" name=\"parcel_$i\"";
312       if ($order->getParcel() & (1 << $i)) echo " checked";
313       echo ">$parcel_contents[$i]\n";
314     }
315     echo "</td>\n";
316     echo "</tr>\n";
317
318     /* Driver. */
319     echo "<tr>\n";
320     echo "  <td>Driver</td>\n";
321     $contacts = get_city_drivers();
322     if (count($contacts)) {
323       echo "  <td><select name=\"driver_id\">\n";
324       option("driver_id", null, "");
325       foreach ($contacts as $contact) {
326         option("driver_id", $contact->getId(), $contact->getDisplayname(), $driver_id);
327       }
328       echo "</select></td>\n";
329     }
330     else echo "  <td>No drivers!</td>\n";
331     echo "</tr>\n";
332
333     /* State. */
334     if ($order->getId()) {
335       echo "<tr>\n";
336       echo "  <td>State</td>\n";
337       echo "  <td><select name=\"state\">\n";
338       for ($i = 0; $i < count($states); $i++) {
339         option("state", $i << 1, ucfirst($states[$i]), $state);
340       }
341       echo "</select></td>\n";
342       echo "</tr>\n";
343     }
344   }
345
346   function show_new_order_form($area_id = null) {
347     if (! check_admin(1)) return;
348
349     $area = get_area_by_id($area_id);
350     if (! count($area)) {
351       echo "<p>No such <a href=\"/area\">area</a>!</p>\n";
352       return;
353     }
354
355     echo "<form method=\"POST\" action=\"" . $_SERVER['REQUEST_URI'] . "\">\n";
356     echo "<p>Place an order:</p>\n";
357
358     echo "<table>\n";
359     show_order_form(null, $area_id);
360
361     echo "<tr>\n";
362     echo "  <td colspan=2>"; submit("add_order", "Order"); echo "</td></tr>\n";
363     echo "</tr>\n";
364     echo "</table>\n";
365     echo "</form>\n";
366   }
367
368   function show_contact_order_form($contact) {
369     if (! check_admin(1)) return;
370
371     $area = get_contact_area($contact);
372     if (! $area) {
373       echo "<p>No valid <a href=\"/area\">area</a> for contact!</p>\n";
374       return;
375     }
376
377     $order = new Order;
378     $order->setBeneficiaryId($contact->getId());
379
380     echo "<form method=\"POST\" action=\"" . $_SERVER['REQUEST_URI'] . "\">\n";
381     echo "<p>Placing order for " . $contact->getStrongLink() . ".";
382     $parcel = $contact->getParcel();
383     if ($parcel) {
384       echo "  Suggested parcel type is <span class=\"strong\">" .  get_contact_parcel_string($contact) . "</span>";
385       $order->setParcel($parcel);
386     }
387     echo "</p>\n";
388
389     echo "<table>\n";
390     show_order_form($order, $area_id);
391
392     echo "<tr>\n";
393     echo "  <td colspan=2>"; submit("add_order", "Order"); echo "</td></tr>\n";
394     echo "</tr>\n";
395     echo "</table>\n";
396     echo "</form>\n";
397   }
398
399   function show_add_new_order_form() {
400     if (! check_admin(1)) return;
401
402     /* We intentionally hide areas with no contacts. */
403     $areas = get_city_areas_with_contacts(null, $GLOBALS['ROLE_BENEFICIARY']);
404     if (! count($areas)) {
405       echo "<p>Can't place any orders until at least one <a href=\"/area\">area</a> has a <a href=\"/contact\">contact</a>!</p>\n";
406       return;
407     }
408
409     echo "<form method=\"POST\" action=\"" . $_SERVER['REQUEST_URI'] . "\">\n";
410     echo "<p>Place an order in <select name=\"area_id\">\n";
411     foreach ($areas as $area) {
412       option("area_id", $area->getId(), get_area_displayname($area));
413     }
414     echo "</select>";
415     submit("show_add_order", "Proceed");
416     echo "</p>\n";
417     echo "</form>\n";
418   }
419
420   function update_order(&$order, $new = false) {
421     global $user_id, $parcel_sizes, $parcel_contents;
422
423     #$date = ymd_to_iso8601("date");
424     $date = $_POST['date'];
425     $requester_id = $_POST['requester_id'];
426     $beneficiary_id = $_POST['beneficiary_id'];
427     $hub_id = $_POST['hub_id'];
428     $driver_id = $_POST['driver_id'];
429     if (! $driver_id) $driver_id = null;
430     $state = $_POST['state'];
431     if (! $state) $state = $GLOBALS['STATE_PLACED'];
432     $parcel = $_POST['parcel_size'];
433     for ($i = count($parcel_sizes); $i < count($parcel_contents); $i++) {
434       if ($_POST['parcel_' . $i] == "on") $parcel |= (1 << $i);
435     }
436
437     if ($date) {
438       list($y, $m, $d) = explode('-', $date);
439       $then = mktime(0, 0, 0, $m, $d, $y);
440     }
441     else $then = time();
442     /* XXX: check date */
443
444     $requester = get_contact_by_id($requester_id);
445     if (! $requester) {
446       echo "<p>Invalid referrer!</p>\n";
447       return false;
448     }
449
450     $beneficiary = get_contact_by_id($beneficiary_id);
451     if (! $beneficiary) {
452       echo "<p>Invalid beneficiary!</p>\n";
453       return false;
454     }
455
456     if ($hub_id) {
457       $hub = get_hub_by_id($hub_id);
458       if (! $hub) {
459         echo "<p>Invalid hub!</p>\n";
460         return false;
461       }
462     }
463     else $hub_id = null;
464
465     if ($new && isset($_POST['recurrence'])) $recurrence = $_POST['recurrence'];
466     if (! $recurrence) $recurrence = 0;
467
468     $now = time();
469     for ($i = 0; $i <= $recurrence; $i++) {
470       if ($i) {
471         echo "<p>Creating recurrence $i.</p>\n";
472         $order = new Order;
473       }
474
475       $order->setDate($then + 7 * 86400 * $i);
476       $order->setRequesterId($requester_id);
477       $order->setBeneficiaryId($beneficiary_id);
478       $order->setHubId($hub_id);
479       $order->setParcel($parcel);
480
481       /* XXX: begin/commit */
482       try {
483         $order->save();
484
485         $order_state = new OrderState;
486         $order_state->setUpdated($now);
487         $order_state->setOrderId($order->getId());
488         $order_state->setUserId($user_id);
489         $order_state->setDriverId($driver_id);
490         $order_state->setState($state);
491
492         $order_state->save();
493       }
494       catch (Exception $e) {
495         if ($new) echo "<p>Error placing order.</p>\n";
496         else echo "<p>Error updating order.</p>\n";
497         echo "<p>" . $e->getMessage() . "</p>\n";
498         return false;
499       }
500     }
501
502     return true;
503   }
504
505   function add_order() {
506     if (! check_admin(1, "place an order")) return;
507
508     $order = new Order;
509     if (! update_order($order, true)) return false;
510     return $order->getId();
511   }
512
513   function delete_order($id = null) {
514     if (! check_admin(1, "delete an order")) return;
515
516     if (isset($id)) $order = get_order_by_id($id);
517     if (! $order) return false;
518
519     try {
520       $q = new OrderStateQuery;
521       $order_states = $q->filterByOrderId($id)->find();
522       foreach ($order_states as $order_state) $order_state->delete();
523       $order->delete();
524       echo "<p>Deleted order.</p>\n";
525     }
526     catch (Exception $e) {
527       echo "<p>Error deleting order $id!</p>\n";
528       return false;
529     }
530
531     return true;
532   }
533
534   function show_order_history($id) {
535     global $states;
536
537     $q = new OrderStateQuery();
538     $order_states = $q->filterByOrderId($id)->orderById()->find();
539
540     if (! count($order_states)) return;
541
542     echo "<h3>Order history</h3>\n";
543     foreach ($order_states as $order_state) {
544       $date = $order_state->getUpdated();
545
546       $user = get_contact_by_id($order_state->getUserId());
547       if ($user) $username = $user->getDisplayname();
548       else $username = "unknown user";
549
550       $driver_id = $order_state->getDriverId();
551       if ($driver_id) $driver = get_contact_by_id($driver_id);
552       else $driver = null;
553
554       /* XXX */
555       $state = $order_state->getState();
556       for ($i = 0; $i < count($states); $i++) {
557         if ((1 << $i) == $state) {
558           $state = $states[$i];
559           break;
560         }
561       }
562       #$state = $states[$order_state->getState()];
563       echo "<p><strong>$username</strong> changed order to state <strong>$state</strong>";
564       if ($driver) echo " for driver " . $driver->getDisplayname();
565       echo " on $date.</p>\n";
566     }
567   }
568
569   function show_order(&$id = null) {
570     if (isset($id)) $order = get_order_by_id($id);
571     if (! $order) return;
572
573     echo "<form method=\"POST\" action=\"" . $_SERVER['REQUEST_URI'] . "\">\n";
574     echo "<p>Order: <span class=\"strong\">" . $order->getId() . "</span>";
575     if (check_admin(1)) {
576       echo " " . $order->getDeleteLink();
577     }
578     echo ": ";
579     echo "\n</p>";
580
581     echo "<table>\n";
582     show_order_form($order);
583
584     if (check_admin(1)) {
585       echo "<tr>\n";
586       echo "  <td colspan=2>";
587       submit("update_order", "Update");
588       echo "</td>\n";
589       echo "</tr>\n";
590     }
591
592     echo "</table>\n";
593     echo "</form>\n";
594
595     show_order_history($order->getId());
596   }
597
598   $state_mask = null;
599   if (count($parameters)) {
600     for ($i = 1; $i < count($parameters); $i++) {
601       if ($parameters[$i] == "state") {
602         /* /order/state/placed+picked */
603         $state_mask = get_order_state_mask($parameters[$i + 1]);
604       }
605     }
606
607     if ($parameters[0] == "in") {
608       /* /order/in/area/Romsey+Town/1 */
609       switch ($parameters[1]) {
610       case "area":
611         case "area":
612           $area_id = $parameters[3];
613           $_POST['area_id'] = $area_id;
614           $q = new AreaQuery;
615           $area = $q->findOneById($area_id);
616           $city = get_area_city($area);
617           if ($city) $city_id = $city->getId();
618           show_area_orders(0, 10, $parameters[2], $area_id, $state_mask);
619         break;
620
621         case "city":
622           $city_id = $parameters[3];
623           $_POST['city_id'] = $city_id;
624           $q = new CityQuery;
625           $city = $q->findOneById($city_id);
626           show_city_orders(0, 10, $parameters[2], $city_id, $state_mask);
627         break;
628       }
629     }
630     else if ($parameters[0] == "from") {
631       /* /order/from/referrer/Iain+Patterson/4 */
632       switch ($parameters[1]) {
633         case "referrer":
634           $contact_id = $parameters[3];
635           $q = new ContactQuery;
636           $contact = $q->findOneById($contact_id);
637           show_requester_orders(0, 10, $parameters[2], $contact_id, $state_mask);
638         break;
639       }
640     }
641     else if ($parameters[0] == "to") {
642       /* /order/to/beneficiary/Cambridge+Community+Church/1 */
643       switch ($parameters[1]) {
644         case "beneficiary":
645           $contact_id = $parameters[3];
646           $q = new ContactQuery;
647           $hub = $q->findOneById($contact_id);
648           show_beneficiary_orders(0, 10, $parameters[2], $contact_id, $state_mask);
649         break;
650       }
651     }
652     else if ($parameters[0] == "place") {
653       if ($parameters[1] == "for") {
654         if ($parameters[2] == "beneficiary") {
655           if ($parameters[4]) $contact = get_contact_by_id($parameters[4]);
656           if (! $contact) $contact = get_contact_by_name(urldecode($parameters[3]));
657           if ($contact) show_contact_order_form($contact);
658         }
659       }
660     }
661   }
662   list($ignored, $id, $args) = parse_parameters($parameters);
663   //echo "<p>$name($id) " . print_r($args, true) . "</p>\n";
664   if (count($args)) {
665     switch ($args[0]) {
666       case "delete":
667         delete_order($id);
668       break;
669     }
670   }
671   else if (isset($id)) show_order($id);
672   else if ($state_mask) show_orders(0, 10, null, null, $state_mask);
673   else {
674     /* XXX: Shown after adding. */
675     show_order_forms($city_id, $state_mask);
676     show_add_new_order_form($city_id);
677   }
678
679   if (count($parameters)) {
680     show_order_forms($city_id, $state_mask);
681   }
682
683
684 ?>