Basic split, seems to work (and save).
[order_line_extra.git] / includes / splitter.inc
1 <?php
2 require_once($path_to_root.'/'.'includes/date_functions.inc');
3 class SqlSet {
4         protected $pairs;
5         function __construct($set=null) {
6                 $this->pairs = $set ?  array_merge($set->pairs) : array();
7         }
8
9         function add($value, $field, $quote=true) {
10                 if($value !=null)  {
11                         array_push($this->pairs, $quote ? "$field = '$value'" : "$field = $value");
12                 }
13
14                 return $this;
15         }
16
17         function addDate($date, $field) {
18                 return $this->add(date2sql($date), $field);
19         }
20
21         function toString() {
22                 return implode(', ', $this->pairs);
23         }
24         
25 }
26 class Split {
27         public $start_date;
28         public $end_date;
29         public $quantity=0;
30         public $quantity_dispatched=0;
31         
32
33         function __construct($start_date, $period=null) {
34                         $this->start_date = $start_date;
35                         $this->end_date = $start_date;
36                         if($period) $this->extend($period);
37         }
38
39         function extend($days) {
40                         $this->end_date = add_days($this->end_date, $days);
41         }
42 }
43
44 class Splitter {
45         public $detail_ids = array() ;
46         public $start_date;
47         public $end_date;
48         protected $days;
49         public $max_quantity;
50
51         public function __construct(array $data) {
52                 $this->detail_ids = array();
53                 foreach($data['detail'] as $detail_id => $detail) {
54                         array_push($this->detail_ids, $detail_id);
55                 }
56                 $this->start_date = $data['start_date'];
57                 $this->end_date = $data['end_date'];
58                 $this->days = date_diff2($this->end_date, $this->start_date, 'd');
59
60                 $this->max_quantity = $data['max_quantity'];
61         }
62
63         protected function loadDetail($detail_id) {
64                 $sql = "SELECT *
65                                                 FROM ".TB_PREF."sales_order_details
66                                                 WHERE id = $detail_id";
67                 $result = db_query($sql);
68                 return db_fetch($result);
69         }
70
71         public function splitAll() {
72                 $ok =  true;
73                 foreach($this->detail_ids as $detail_id) {
74 display_warning("processing $detail_id");
75                                 $detail = $this->loadDetail($detail_id);
76                                 $splits = $this->split($detail);
77                                 $ok &= $this->saveSplits($detail, $splits);
78                 }
79
80                 return $ok; 
81         }
82
83         public function days() {
84                 return $this->days;
85         }
86
87
88         
89         /* This function splits on order detail in bits of a specified size.
90  * Each split  starting at the end time of the previous one.
91  * the first split starts at the start_date and ends at the end_date.
92  * The 'splitting' split the whole quantity not only the quantity left to dispatch
93  * However, fully dispatched split won't be split in real, but merged with the next one
94  */
95         public function split($row) {
96                 $splits = array();
97                 $quantity = $row['quantity'];
98                 $quantity_sent = $row['qty_sent'];
99
100                 /* Check if the item has been fully dispatched.
101                  * If so,  there is no need to do anything.
102                  */
103                 if($quantity_sent >= $quantity) return $splits;
104                 
105                 // determine the number of split needed. This will give us the lenght of a split.
106                 $nsplit = ceil($quantity/$this->max_quantity);
107
108                 $period = $this->days/$nsplit;
109
110                 array_push($splits, $split = new Split($this->start_date, $period));
111                 $split->quantity_dispatched = $quantity_sent;
112                 $split_quantity = min($quantity, $this->max_quantity);
113                 while($split_quantity > 0) {
114                                         $split->quantity += $split_quantity;
115                                 // Check if the split has been entirely dispatch or not.
116                                 if($split->quantity_dispatched > $split->quantity) {
117                                         //extend the split
118                                         $split->extend($period);
119                                 }
120                                 else {
121                                         // create a new split
122                                         array_push($splits, $split = new Split($split->end_date, $period));
123                                         
124                                 }
125                                 $quantity -= $split_quantity;
126                                 $split_quantity = min($quantity, $this->max_quantity);
127                 }
128
129                 // We need to remove the last split if it's empty
130                 if($split->quantity == 0) array_pop($splits);
131                 return $splits;
132         }
133
134         public function saveSplits($detail, $splits) {
135                 if(empty($splits)) return true;
136
137                 $detail_id = $detail['id'];
138                 $priority = $detail['priority'];
139                 $order_no = $detail['order_no'];
140                 $trans_type = $detail['trans_type'];
141
142                 /* We need to update the first one (as it exists already in the database)
143                  * but insert the following one.
144                  */
145                 $first = array_shift($splits);
146                 $set = new SqlSet();
147                 $set->addDate($first->start_date, 'hold_until_date')
148                                 ->addDate($first->end_date, 'expiry_date')
149                                 ->add($first->quantity, 'quantity', false)
150                                 ->add($priority, 'priority');
151         display_warning($set->toString());
152                 db_query("UPDATE ".TB_PREF."sales_order_details
153                                                         SET {$set->toString()}
154                                                         WHERE id = $detail_id", "Problem splitting order details $detail_id");
155
156                 // Compute common field for each split
157                 $common_set  = new SqlSet();
158                 $common_set->add($order_no, 'order_no', false)
159                                 ->add($priority, 'priority')
160                                 ->add($trans_type, 'trans_type')
161                                 ->add($detail['required_date'], 'required_date')
162                                 ->add($detail['comment'], 'comment')
163                                 ->add($detail['stk_code'], 'stk_code')
164                                 ->add($detail['description'], 'description')
165                                 ->add($detail['unit_price'], 'unit_price', false)
166                                 ->add($detail['discount_percent'], 'discount_percent', false);
167
168                 foreach($splits as $split) {
169                         $set = new SqlSet($common_set);
170                         $set->addDate($split->start_date, 'hold_until_date')
171                                 ->addDate($split->end_date, 'expiry_date')
172                                 ->add($split->quantity, 'quantity', false);
173         display_warning($set->toString());
174                 db_query("INSERT INTO ".TB_PREF."sales_order_details
175                                                         SET {$set->toString()}"
176                                                  ,"Problem spliting order details $detail_id");
177                 }
178
179                 return true;
180                 
181
182         }
183
184 }
185 ?>