84aa9ce6372595ca380371e985178753dea4dace
[fa-stable.git] / taxes / tax_calc.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 include_once($path_to_root . "/taxes/db/tax_groups_db.inc");
13 include_once($path_to_root . "/taxes/db/tax_types_db.inc");
14 include_once($path_to_root . "/taxes/db/item_tax_types_db.inc");
15
16 //---------------------------------------------------------------------------------
17 // Returns basic fiscal parameters for transaction item which depend on stock type and customer/supplier tax area.
18 //
19 // vat_category => stock tax category 
20 // tax_area => cust/supp tax area
21 // taxes => all taxes applicable:
22 //      tax_type_id1 => (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate)
23 //      tax_type_id2 => ...
24
25 function get_base_taxdata($stock_id, $group_id)
26 {
27         static $last_group = null, $group_data;
28
29         if ($last_group != $group_id) { // cache group data for better performance
30                 $last_group = $group_id;
31                 $group_data = get_tax_group_data($group_id);
32         }
33
34         $taxdata = array('tax_area' => $group_data['tax_area'], 'taxes' => null);
35
36         if ($stock_id===null) // shipping special case
37         {
38                 $taxdata['vat_category'] = VC_SERVICES;
39                 $taxdata['taxes'] = get_tax_group_items_as_array(null);
40                 return $taxdata;
41         }
42
43         $item_tax_type = get_item_tax_type_for_item($stock_id); // get item tax data
44         $taxdata['vat_category'] = $item_tax_type['vat_category'];
45
46         // if the item is exempt from all taxes thats all
47         if ($item_tax_type["exempt"])
48                 return $taxdata;
49
50         $taxdata['taxes'] = array();
51
52         // get the exemptions for this item tax type
53         $item_tax_type_exemptions_db = get_item_tax_type_exemptions($item_tax_type["id"]);
54
55         // read them all into an array to minimize db querying
56         $item_tax_type_exemptions = array();
57         while ($item_tax_type_exemp = db_fetch($item_tax_type_exemptions_db)) 
58         {
59                 $item_tax_type_exemptions[] = $item_tax_type_exemp["tax_type_id"];
60         }
61
62         $tax_group_items_array = $group_data['taxes'];
63         // if any of the taxes of the tax group are in the exemptions, then skip
64         foreach ($tax_group_items_array as $tax_group_item) 
65         {
66
67                 $skip = false;
68
69                 // if it's in the exemptions, skip
70                 if (is_null($tax_group_item['rate']))
71                         $skip = true;
72                 else
73                         foreach ($item_tax_type_exemptions as $exemption) 
74                         {
75                                 if (($tax_group_item['tax_type_id'] == $exemption)) 
76                                 {
77                                 $skip = true;
78                                 break;
79                                 }
80                         }
81
82                 if (!$skip) 
83                 {
84                         $index = $tax_group_item['tax_type_id'];
85                         $taxdata['taxes'][$index] = $tax_group_item;
86                 }
87         }
88
89         return $taxdata;
90 }
91
92 /*
93         Main tax procedure splitting transaction item value according to item tax rules applicable:
94                 $stock_id - stock item code; special case is '' for shipping
95                 $amount - price/value to be splitted
96                 $tax_group - entity tax group
97                 $tax_included - whether value includes all taxes
98                 $vat_factor - 0-1; tax deduction factor (purchases, not used for now; depends on whthere it is related to exempt or taxed sales)
99                 $allow_reverse - option for invoice - whether to honour reverse charging (depends on customer tax status)
100
101         Returned array contains calculated values for GL postings and tax registration:
102                 'Net' - value without tax,
103                 'Tax' - tax sum,
104                 'Cost' - cost value (can be higher then Net value),
105                 'vat_category' -  stock tax category
106
107         and (with numeric keys) detailed info for any applicable tax rate:
108                 'tax_type_id' - tax type id
109                 'Value' - charged tax value
110                 'Deductible' - tax deductible (can be lower than Value for special goods or mixed sales structure)
111                 'Payable' - tax payable
112                 'Adjust' - additional adjustment to deductible tax due to sales structure factor
113                 'rate' - tax rate
114                 'sales_gl_code' - sales tax GL account
115                 'purchasing_gl_code' - purchase tax GL account
116                 'tax_type_name' - name of tax type
117
118         Price value is splitted as follows:
119                 Tax: sum of Value (for applicable taxes)
120                 Net: amount - Tax
121                 Cost: Net + sum(Payable-Deductible)
122          for every applicable tax rate:
123                 Value: tax calculated or 0
124                 Deductible: vat_factor*Value or 0
125                 Adjust: Value-Deductible or 0
126                 Payable: 0 or Value
127 */
128 function split_item_price($stock_id, $amount, $group_id, $tax_included=false, $trans_type=ST_SUPPINVOICE, $vat_factor = 1, 
129         $allow_reverse=true)
130 {
131         global $TS;
132
133         $dec = user_price_dec();
134
135         $itemdata = get_base_taxdata($stock_id, $group_id);
136         $vat_category = $itemdata['vat_category'];
137         $item_taxes = $itemdata['taxes'];
138
139         $taxopt = $TS->options($trans_type, $itemdata['tax_area'], $vat_category, $allow_reverse);
140
141         if (empty($item_taxes))
142         {
143                   $ret_array['Net'] = $amount;
144                   $ret_array['Cost'] = $amount;
145                   $ret_array['Tax'] = 0;
146                   if (!is_null($item_taxes))    // register empty taxes only for not fully exempt items
147                                 $ret_array[] = array('Value'=>0, 'rate' => null, 'tax_type_id' => null, 'Deductible'=>0, 'Adjust' => 0, 'Payable' => 0);
148         }
149         else
150         {
151                 $ret_array['Net'] = $ret_array['Cost'] = $ret_array['Tax'] = 0;
152
153                 $tax_multiplier = 0;
154
155                 if ($taxopt&TAX_CHARGED)        // divide tax for net and tax only if charged on document
156                         foreach ($item_taxes as $taxitem) 
157                                 $tax_multiplier += $taxitem['rate'];
158
159                 $partial_vat_percent = get_company_pref('partial_vat_percent');
160
161                 foreach ($item_taxes as $tax_id => $item_tax) 
162                 {
163                                 if ($item_tax['rate'] !== null)
164                                 {
165
166                                         // effective vat for some special purchases is lower than nominal
167                                         $factor = $vat_category == VC_NONDEDUCT ? 0 : ($vat_category==VC_PARTIAL ? $partial_vat_percent/100.0 : 1);
168
169                                         $net_value = $amount;
170                                         if ($tax_included == true) {
171                                                 $vat_value = round($amount*$item_tax['rate']/(100+$tax_multiplier), 2);
172
173                                                 if ($taxopt&TAX_CHARGED)
174                                                         $net_value -= $vat_value;
175
176                                         } else {
177
178                                                 $vat_value = round($amount * $item_tax['rate'] / 100, 2);
179                                         }
180
181                                         $ret_array['Net'] = round2($net_value, $dec);
182                                 $ret_array['Cost'] = $ret_array['Net'];
183
184                                         $tax = array('Value' => 0, 'Deductible' => 0, 'Adjust' => 0, 'Payable' => 0);
185
186                                 $tax['purchasing_gl_code'] = $item_tax['purchasing_gl_code'];
187                                 $tax['sales_gl_code'] = $item_tax['sales_gl_code'];
188                                 $tax['rate'] = $item_tax['rate'];
189                                         $tax['tax_type_id'] = $item_tax['tax_type_id'];
190                                         $tax['tax_type_name'] = $item_tax['tax_type_name'];
191
192                                         if ($taxopt & TAX_CHARGED)                                                      // tax is charged on document
193                                                 $tax['Value'] =  round2($vat_value, $dec);
194
195                                         if ($taxopt & TAX_PAYABLE)                                                       // transaction is taxable
196                                                 $tax['Payable'] =  round2($vat_value, $dec);
197
198                                 if ($taxopt & TAX_DEDUCTIBLE) // tax is deductible
199                                 {
200                                                 $tax['Deductible'] = round2($vat_value*$factor, 2); // avoid rounding issues if $dec > 2 decimal places
201                                                 $tax['Adjust'] = round2(-(1-$vat_factor)*$factor*$vat_value, $dec); // adjustment due to mixed taxed/exmpt sales activity
202                                     } else {
203                                                 $tax['Deductible'] = 0;
204                                                 $tax['Adjust'] = 0;
205                                         }
206
207                                         if ($tax['Payable'])
208                                         $ret_array['Cost'] += ($tax['Payable'] - $tax['Deductible']);
209                                     elseif ($tax['Deductible'])
210                                         $ret_array['Cost'] += $tax['Adjust'];
211
212                                 $ret_array[] = $tax;
213                                         $ret_array['Tax'] += $tax['Value'];
214                                 }
215                 }
216         }
217     $ret_array['vat_category'] = $vat_category;
218         return $ret_array;
219 }
220
221 //-----------------------------------------------------------------------------------
222 // return an array of (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate, included_in_price, Value, Net)
223 //
224 // $vat_factors - effective part of vat values included in tax; calculated but not included vat is added to net value
225 //
226 function get_tax_for_items($trans_type, $items, $prices, $shipping_cost, $tax_group, $tax_included=null,
227         $tax_algorithm = null, $vat_factors = null, $allow_reverse = true)
228 {
229
230         // if shipping cost is passed, just add to the prices/items tables
231         if ($shipping_cost != 0)
232         {
233                 $items[] = null;
234                 $prices[] = $shipping_cost;
235                 if ($vat_factors)
236                         $vat_factors[] = 1;
237         }
238
239         // calculate tax sums
240         $ret_tax_array = array();
241         foreach($items as $i => $stock_id)
242         {
243                 $taxdata = split_item_price($stock_id, $prices[$i], $tax_group, $tax_included, $trans_type,
244                          $vat_factors ? $vat_factors[$i] : 1, $allow_reverse);
245
246                 foreach ($taxdata as $key => $data)
247                 {
248                         if (is_numeric($key))
249                         {
250                                 $tax_id = isset($data['tax_type_id']) ? $data['tax_type_id'] : 'exempt';
251
252                                 if (!isset($ret_tax_array[$tax_id]))
253                                 {
254                                         $ret_tax_array[$tax_id] = $data;
255                                         $ret_tax_array[$tax_id]['Net'] = $taxdata['Net'];
256                                         $ret_tax_array[$tax_id]['vat_category'] = $taxdata['vat_category'];
257                                 }
258                                 else
259                                 {
260                                         foreach(array('Value', 'Payable', 'Deductible', 'Adjust') as $amt)
261                                                 $ret_tax_array[$tax_id][$amt] += $data[$amt];
262                                         $ret_tax_array[$tax_id]['Net'] += $taxdata['Net'];
263                                 }
264                         }
265                 }
266         }
267
268         if (!$tax_algorithm)
269                 $tax_algorithm = get_company_pref('tax_algorithm');
270
271         if ($tax_algorithm == TCA_TOTALS) { // ?
272                 $dec = user_price_dec();
273                 // update taxes with 
274                 foreach($ret_tax_array as $index => $item_tax) {
275                         if ($ret_tax_array[$index]['Value'])
276                                 $ret_tax_array[$index]['Value'] = round2($item_tax['Net'] * $item_tax['rate'] / 100, $dec);
277                 }
278         }
279
280         return $ret_tax_array;
281 }
282
283
284 //---------------------------------------------------------------------------------
285
286 // returns the price of a given item minus any included taxes
287 // for item $stock_id with line price $price and supplier/customer group_id $tax_group
288
289 function get_tax_free_price_for_item($trans_type, $stock_id, $price, $tax_group, $tax_included, $allow_reverse = true)
290 {
291         // if price is zero, then can't be taxed !
292         if ($price == 0)
293                 return 0;
294
295         if ($tax_included==0) return $price;
296
297         $taxdata = split_item_price($stock_id, $price, $tax_group, $tax_included, $trans_type, 1, $allow_reverse);
298
299         return $taxdata['Net'];
300 }
301 //
302 //      Full price (incl. VAT) for item $stock_id
303 //      calculated for line price $price, and applicable group $tax_group
304 //
305 function get_full_price_for_item($trans_type, $stock_id, $price, $tax_group, $tax_included, $allow_reverse = true)
306 {
307         // if price is zero, then can't be taxed !
308         if ($price == 0)
309                 return 0;
310
311         if ($tax_included==1) return $price;
312
313         $taxdata = split_item_price($stock_id, $price, $tax_group, $tax_included, $trans_type, 1, $allow_reverse);
314
315         return $taxdata['Net'] + $taxdata['Tax'];
316 }