Use hold_until_date in item_scheduler.
[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         public $start_offset;
49         public $end_offset;
50         protected $days;
51         public $max_quantity;
52
53         public function __construct(array $data) {
54                 $this->detail_ids = array();
55                 foreach($data['detail'] as $detail_id => $detail) {
56                         array_push($this->detail_ids, $detail_id);
57                 }
58                 $this->start_date = $data['start_date'];
59                 $this->end_date = $data['end_date'];
60                 $this->start_offset = $data['start_offset'];
61                 $this->end_offset = $data['end_offset'];
62                 $this->days = date_diff2($this->end_date, $this->start_date, 'd');
63
64                 $this->max_quantity = $data['max_quantity'];
65         }
66
67         protected function loadDetail($detail_id) {
68                 $sql = "SELECT *
69                                                 FROM ".TB_PREF."sales_order_details
70                                                 WHERE id = $detail_id";
71                 $result = db_query($sql);
72                 return db_fetch($result);
73         }
74
75         public function splitAll() {
76                 $ok =  true;
77                 foreach($this->detail_ids as $detail_id) {
78 display_warning("processing $detail_id");
79                                 $detail = $this->loadDetail($detail_id);
80                                 $splits = $this->split($detail);
81                                 foreach($splits as $split) $this->alterSplit($split);
82                                 $ok &= $this->saveSplits($detail, $splits);
83                 }
84
85                 return $ok; 
86         }
87
88         public function days() {
89                 return $this->days;
90         }
91
92
93         
94         /* This function splits on order detail in bits of a specified size.
95  * Each split  starting at the end time of the previous one.
96  * the first split starts at the start_date and ends at the end_date.
97  * The 'splitting' split the whole quantity not only the quantity left to dispatch
98  * However, fully dispatched split won't be split in real, but merged with the next one
99  */
100         public function split($row) {
101                 $splits = array();
102                 $quantity = $row['quantity'];
103                 $quantity_sent = $row['qty_sent'];
104
105                 /* Check if the item has been fully dispatched.
106                  * If so,  there is no need to do anything.
107                  */
108                 if($quantity_sent >= $quantity) return $splits;
109                 
110                 // determine the number of split needed. This will give us the lenght of a split.
111                 $nsplit = ceil($quantity/$this->max_quantity);
112
113                 $period = $this->days/$nsplit;
114
115                 array_push($splits, $split = new Split($this->start_date, $period));
116                 $split->quantity_dispatched = $quantity_sent;
117                 $split_quantity = min($quantity, $this->max_quantity);
118                 while($split_quantity > 0) {
119                                         $split->quantity += $split_quantity;
120                                 // Check if the split has been entirely dispatch or not.
121                                 if($split->quantity_dispatched > $split->quantity) {
122                                         //extend the split
123                                         $split->extend($period);
124                                 }
125                                 else {
126                                         // create a new split
127                                         array_push($splits, $split = new Split($split->end_date, $period));
128                                         
129                                 }
130                                 $quantity -= $split_quantity;
131                                 $split_quantity = min($quantity, $this->max_quantity);
132                 }
133
134                 // We need to remove the last split if it's empty
135                 if($split->quantity == 0) array_pop($splits);
136                 return $splits;
137         }
138
139         function alterSplit($split) {
140                 foreach(explode(' ', 'start end') as $att) {
141                         $date = "${att}_date";
142                         $offset = "${att}_offset";
143                         if($this->$offset === null || $this->$offset === "") {
144                                 $split->$date = null;
145                         }
146                         else {
147                                 $split->$date = add_days($split->$date, $this->$offset);
148                         }
149                 }
150         }
151
152         public function saveSplits($detail, $splits) {
153                 if(empty($splits)) return true;
154
155                 $detail_id = $detail['id'];
156                 $priority = $detail['priority'];
157                 $priority_offset = 1;
158                 $order_no = $detail['order_no'];
159                 $trans_type = $detail['trans_type'];
160
161                 /* We need to update the first one (as it exists already in the database)
162                  * but insert the following one.
163                  */
164                 $first = array_shift($splits);
165                 $set = new SqlSet();
166                 $set->addDate($first->start_date, 'hold_until_date')
167                                 ->addDate($first->end_date, 'expiry_date')
168                                 ->add($first->quantity, 'quantity', false);
169         display_warning($set->toString());
170                 db_query("UPDATE ".TB_PREF."sales_order_details
171                                                         SET {$set->toString()}
172                                                         WHERE id = $detail_id", "Problem splitting order details $detail_id");
173
174                 // Compute common field for each split
175                 $common_set  = new SqlSet();
176                 $common_set->add($order_no, 'order_no', false)
177                                 ->add($trans_type, 'trans_type')
178                                 ->add($detail['required_date'], 'required_date')
179                                 ->add($detail['comment'], 'comment')
180                                 ->add($detail['stk_code'], 'stk_code')
181                                 ->add($detail['description'], 'description')
182                                 ->add($detail['unit_price'], 'unit_price', false)
183                                 ->add($detail['discount_percent'], 'discount_percent', false);
184
185                 foreach($splits as $split) {
186                         $set = new SqlSet($common_set);
187                         $set->addDate($split->start_date, 'hold_until_date')
188                                 ->addDate($split->end_date, 'expiry_date')
189                                 ->add($split->quantity, 'quantity', false);
190         display_warning($set->toString());
191                 db_query("INSERT INTO ".TB_PREF."sales_order_details
192                                                         SET {$set->toString()}
193                                                         , priority = '$priority' + INTERVAL ${priority_offset} second"
194                                                  ,"Problem spliting order details $detail_id");
195                                 $priority_offset++;
196                 }
197
198                 return true;
199                 
200
201         }
202
203 }
204 ?>