Rewritten sales shipping cost taxation, improved shipping cost handling in sales...
[fa-stable.git] / purchasing / includes / po_class.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 /*
13         This class serves as cart for PO or GRN.
14 */
15
16 class purch_order 
17 {
18         // db interface
19         var $trans_type; // order/grn/invoice (direct)
20         var $supplier_id;
21         var $Comments;
22         var $tran_date;
23         var $reference;
24         var $supp_ref;
25         var $Location;
26         var $delivery_address;
27
28         var $prep_amount = 0; // prepayment required
29         var $alloc; // sum of payments allocated
30         var $tax_included; // type of prices
31
32         var $due_date;          // for grn this is delivery date
33         var $order_no;          // for order modification, grn batch
34         var $ex_rate;           // for grn
35
36         var $line_items;
37         //----
38
39         var $curr_code;
40         var $supplier_name;
41         var $credit;
42         var $tax_group_id;
43         var $terms;
44         var $cash_account;
45         var $dimension,
46                 $dimension2;
47
48         var $tax_overrides = array();           // array of taxes manually inserted during sales invoice entry (direct invoice)
49
50         var $prepayments = array();
51
52         var $fixed_asset = false;
53         var $grn_id;    // grn batch id used in edition only
54
55         function __construct()
56         {
57                 $this->line_items = array();
58                 $this->order_no = $this->supplier_id = 0;
59                 $this->tax_group_id = find_domestic_tax_group(); // prevent tax errors until supplier is selected
60         }
61         
62         function set_supplier($supplier_id, $supplier_name, $curr_code, $tax_group_id, $tax_included, $tax_area)
63         {
64                 $this->supplier_id = $supplier_id;
65                 $this->supplier_name = $supplier_name;
66                 $this->curr_code = $curr_code;
67                 $this->tax_group_id = $tax_group_id;
68                 $this->tax_included = $tax_included;
69                 $this->tax_area = $tax_area;
70         }
71         
72         function add_to_order($stock_id, $qty, $item_descr, $price, $req_del_date, $qty_inv, $qty_recd, $qty_ordered=0, $grn_item_id=0, $po_item_id=0, $unit_cost=0)
73         {
74                 $line = new po_line_details($stock_id, $item_descr, $qty, $price,
75                         $req_del_date, $qty_inv, $qty_recd, $qty_ordered, $grn_item_id, $po_item_id, $unit_cost);
76
77                 $this->line_items[] = $line;
78                 $line->cart = $this;
79         }
80
81         function update_order_item($line_no, $qty, $price, $req_del_date, $description="")
82         {
83                 if ($description != "")
84                         $this->line_items[$line_no]->item_description = $description;
85                 $this->line_items[$line_no]->quantity = $qty;
86                 $this->line_items[$line_no]->price = $price;
87                 $this->line_items[$line_no]->req_del_date = $req_del_date;
88                 $this->line_items[$line_no]->item_description = $description;
89         }
90
91         function remove_from_order($line_no)
92         {
93                 array_splice($this->line_items, $line_no, 1);
94         }
95         
96         function order_has_items() 
97         {
98                 return count($this->line_items) != 0;
99         }
100         
101         function clear_items() 
102         {
103         unset($this->line_items);
104                 $this->line_items = array();
105                 $this->order_no = 0;
106         }
107
108         
109         function any_already_received()
110         {
111                 /* Checks if there have been deliveries or invoiced entered against any of the line items */
112                 if (count($this->line_items) > 0)
113                 {
114                         foreach ($this->line_items as $ordered_items) 
115                         {
116                                 if ($ordered_items->qty_received != 0 || $ordered_items->qty_inv != 0)
117                                 {
118                                         return 1;
119                                 }
120                         }
121                 }
122                 return 0;
123         }
124
125         function some_already_received($line_no)
126         {
127                 /* Checks if there have been deliveries or amounts invoiced against a specific line item */
128                 if (count($this->line_items) > 0)
129                 {
130                         if ($this->line_items[$line_no]->qty_received != 0 || 
131                                 $this->line_items[$line_no]->qty_inv != 0)
132                         {
133                                 return 1;
134                         }
135                 }
136                 return 0;
137         }
138         
139         //
140         //      Returns taxes for PO/GRN.
141         //
142         function get_taxes()
143         {
144                 $items = array();
145                 $prices = array();
146
147                 foreach ($this->line_items as $ln_itm) {
148                         $items[] = $ln_itm->stock_id;
149                         $prices[] = round($ln_itm->price * $ln_itm->quantity,  user_price_dec());
150                 }
151                 $taxes = get_tax_for_items($this->trans_type, $items, $prices, $this->tax_group_id, $this->tax_included);
152
153         // Adjustment for swiss franken, we always have 5 rappen = 1/20 franken
154             if ($this->curr_code == 'CHF') {
155                         $val = $taxes['1']['Value'];
156                         $val1 = (floatval((intval(round(($val*20),0)))/20));
157                         $taxes['1']['Value'] = $val1;
158                 }
159                 foreach($this->tax_overrides as $id => $value) // add values entered manually
160                 {
161                         $taxes[$id]['Override'] = $value;
162                 }
163                 return $taxes;
164         }
165
166         /*
167                 Returns order value including all taxes
168         */
169         function get_trans_total() {
170                 
171                 $total = 0;
172                 $dec = user_price_dec();
173
174                 foreach ($this->line_items as $ln_itm) {
175                         $items[] = $ln_itm->stock_id;
176                         $value = round($ln_itm->quantity * $ln_itm->price, $dec);
177                         $prices[] =$value;
178                         $total += $value;
179                 }
180
181                 if (!$this->tax_included ) {
182                         $taxes = get_tax_for_items($this->trans_type, $items, $prices, $this->tax_group_id, $this->tax_included);
183
184                         foreach($taxes as $tax)
185                                 $total += round($tax['Value'], $dec);
186                 }
187                 return $total;
188         }
189
190         function split_line_values()
191         {
192                 // split nominal line values
193                 foreach($this->line_items as $line)
194                         $line->split_item_value();
195
196                 // Exact tax values are currently entered as tax totals, so we need to move the differences back on line level.
197                 // currently first item with given tax type will be fixed with the calculated difference
198                 // FIXME: change UI moving tax edit to line level in line edit mode, then this workaround will be obsolete.
199                 foreach($this->get_taxes() as $tax_id => $tax)
200                 {
201                         if ($tax['Value'] != 0 && ($tax['Value'] != $tax['Override']))
202                         {
203                                 foreach($this->line_items as $id => $line)
204                                         if ($line->gl_amounts[0]['tax_type_id'] == $tax_id) // assumed single tax rate on item, so always gl_mount[0] is valid
205                                         {
206                                                 $diff = $tax['Override'] - $tax['Value'];
207                                                 $this->line_items[$id]->gl_amounts[0]['Value'] += $diff;
208                                                 if ($line->vat_category != VC_NONDEDUCT)
209                                                         $this->line_items[$id]->gl_amounts[0]['Deductible'] += $diff;
210                                                 else
211                                                         $this->line_items[$id]->gl_amounts['Cost'] += $diff;
212                                                  // when supplier uses net prices the price is exact, so don't fix net, still record exact VAT.
213                                                 if ($this->tax_included) 
214                                                 {
215                                                         $this->line_items[$id]->gl_amounts['Net'] -= $diff;
216                                                         $this->line_items[$id]->gl_amounts['Cost'] -= $diff;
217                                                 }
218                                                 break;
219                                         }
220                         }
221                 }
222         }
223
224 } /* end of class defintion */
225
226 class po_line_details 
227 {
228         //db interface
229         var $po_item_id;
230         var $grn_item_id;
231         var $stock_id;
232         var $item_description;
233         var $price;
234         var $units;
235         var $req_del_date;
236         var $qty_inv;   // quantity already invoiced against this line (all PIs)
237         var $qty_received;      // quantity already received against this line (all GRNs)
238         var $qty_ordered;       // quantity on order (not used on PO entry)
239         var $unit_cost;
240         var $quantity;          // this document line quantity
241         //---
242
243         var $cart; // line context
244         var $descr_editable;
245         var $vat_category;
246         var $gl_amounts;        // splited line value (after call to split_line_value method)
247
248         function __construct($stock_item, $item_descr, $qty, $prc, $req_del_date, 
249                 $qty_inv, $qty_recd, $qty_ordered=0, $grn_item_id=0, $po_item_id=0, $unit_cost=0)
250         {
251                 $this->stock_id = $stock_item;
252                 $item_row = get_item($stock_item);
253                 if (!$item_row) 
254                         return;
255
256                 $this->descr_editable = $item_row["editable"];
257                 if ($item_descr == null || !$this->descr_editable)
258                         $this->item_description = $item_row["description"];
259                 else
260                         $this->item_description = $item_descr;
261                 $this->quantity = $qty;
262                 $this->req_del_date = $req_del_date;
263                 $this->price = $prc;
264                 $this->units = $item_row["units"];
265                 $this->qty_received = $qty_recd;
266                 $this->qty_inv = $qty_inv;
267                 $this->unit_cost = $unit_cost;
268                 $this->grn_item_id = $grn_item_id;
269                 $this->vat_category = $item_row["vat_category"];
270                 $this->qty_ordered = $qty_ordered;
271                 $this->po_item_id = $po_item_id;
272         }
273         
274         //
275         // GRN line tax free value.
276         //
277         function taxfree_charge_value()
278         {
279                 $this->split_item_value();
280                 return $this->gl_amounts['Net'];
281         }
282
283         /*
284                 Splits item value to parts posted to GL.
285         */
286         function split_item_value()
287         {
288         $vat_factor = 1;
289
290                 return $this->gl_amounts = split_item_price($this->stock_id, $this->price*$this->quantity, $this->cart->tax_group_id, $this->cart->tax_included, 
291                         ST_SUPPINVOICE, $vat_factor);
292         }
293   
294 }
295