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