dolibarr  17.0.4
price.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2002-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2006-2008 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es>
5  * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
6  * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
7  * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  */
22 
86 function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller = '', $localtaxes_array = '', $progress = 100, $multicurrency_tx = 1, $pu_devise = 0, $multicurrency_code = '')
87 {
88  global $conf, $mysoc, $db;
89 
90  $result = array();
91 
92  // Clean parameters
93  if (empty($info_bits)) {
94  $info_bits = 0;
95  }
96  if (empty($txtva)) {
97  $txtva = 0;
98  }
99  if (empty($seller) || !is_object($seller)) {
100  dol_syslog("Price.lib::calcul_price_total Warning: function is called with parameter seller that is missing", LOG_WARNING);
101  if (!is_object($mysoc)) { // mysoc may be not defined (during migration process)
102  $mysoc = new Societe($db);
103  $mysoc->setMysoc($conf);
104  }
105  $seller = $mysoc; // If sell is done to a customer, $seller is not provided, we use $mysoc
106  //var_dump($seller->country_id);exit;
107  }
108  if (empty($localtaxes_array) || !is_array($localtaxes_array)) {
109  dol_syslog("Price.lib::calcul_price_total Warning: function is called with parameter localtaxes_array that is missing or empty", LOG_WARNING);
110  }
111  if (!is_numeric($txtva)) {
112  dol_syslog("Price.lib::calcul_price_total Warning: function was called with a parameter vat rate that is not a real numeric value. There is surely a bug.", LOG_ERR);
113  } elseif ($txtva >= 1000) {
114  dol_syslog("Price.lib::calcul_price_total Warning: function was called with a bad value for vat rate (should be often < 100, always < 1000). There is surely a bug.", LOG_ERR);
115  }
116  // Too verbose. Enable for debug only
117  //dol_syslog("Price.lib::calcul_price_total qty=".$qty." pu=".$pu." remiserpercent_ligne=".$remise_percent_ligne." txtva=".$txtva." uselocaltax1_rate=".$uselocaltax1_rate." uselocaltax2_rate=".$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$ice_base_type.' type='.$type.' progress='.$progress);
118 
119  $countryid = $seller->country_id;
120 
121  if (is_numeric($uselocaltax1_rate)) {
122  $uselocaltax1_rate = (float) $uselocaltax1_rate;
123  }
124  if (is_numeric($uselocaltax2_rate)) {
125  $uselocaltax2_rate = (float) $uselocaltax2_rate;
126  }
127 
128  if ($uselocaltax1_rate < 0) {
129  $uselocaltax1_rate = $seller->localtax1_assuj;
130  }
131  if ($uselocaltax2_rate < 0) {
132  $uselocaltax2_rate = $seller->localtax2_assuj;
133  }
134 
135  //var_dump($uselocaltax1_rate.' - '.$uselocaltax2_rate);
136  dol_syslog('Price.lib::calcul_price_total qty='.$qty.' pu='.$pu.' remise_percent_ligne='.$remise_percent_ligne.' txtva='.$txtva.' uselocaltax1_rate='.$uselocaltax1_rate.' uselocaltax2_rate='.$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$price_base_type.' type='.$type.' progress='.$progress);
137 
138  // Now we search localtaxes information ourself (rates and types).
139  $localtax1_type = 0;
140  $localtax2_type = 0;
141 
142  if (is_array($localtaxes_array) && count($localtaxes_array)) {
143  $localtax1_type = $localtaxes_array[0];
144  $localtax1_rate = $localtaxes_array[1];
145  $localtax2_type = $localtaxes_array[2];
146  $localtax2_rate = $localtaxes_array[3];
147  } else {
148  // deprecated method. values and type for localtaxes must be provided by caller and loaded with getLocalTaxesFromRate using the full vat rate (including text code)
149  // also, with this method, we may get several possible values (for example with localtax2 in spain), so we take the first one.
150  dol_syslog("Price.lib::calcul_price_total search vat information using old deprecated method", LOG_WARNING);
151 
152  $sql = "SELECT taux, localtax1, localtax2, localtax1_type, localtax2_type";
153  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as cv";
154  $sql .= " WHERE cv.taux = ".((float) $txtva);
155  $sql .= " AND cv.fk_pays = ".((int) $countryid);
156  $resql = $db->query($sql);
157  if ($resql) {
158  $obj = $db->fetch_object($resql);
159  if ($obj) {
160  $localtax1_rate = (float) $obj->localtax1; // Use float to force to get first numeric value when value is x:y:z
161  $localtax2_rate = (float) $obj->localtax2; // Use float to force to get first numeric value when value is -19:-15:-9
162  $localtax1_type = $obj->localtax1_type;
163  $localtax2_type = $obj->localtax2_type;
164  //var_dump($localtax1_rate.' '.$localtax2_rate.' '.$localtax1_type.' '.$localtax2_type);
165  }
166  } else {
167  dol_print_error($db);
168  }
169  }
170 
171  // pu calculation from pu_devise if pu empty
172  if (empty($pu) && !empty($pu_devise)) {
173  if (!empty($multicurrency_tx)) {
174  $pu = $pu_devise / $multicurrency_tx;
175  } else {
176  dol_syslog('Price.lib::calcul_price_total function called with bad parameters combination (multicurrency_tx empty when pu_devise not) ', LOG_ERR);
177  return array();
178  }
179  }
180  if ($pu === '') {
181  $pu = 0;
182  }
183  // pu_devise calculation from pu
184  if (empty($pu_devise) && !empty($multicurrency_tx)) {
185  if (is_numeric($pu) && is_numeric($multicurrency_tx)) {
186  $pu_devise = $pu * $multicurrency_tx;
187  } else {
188  dol_syslog('Price.lib::calcul_price_total function called with bad parameters combination (pu or multicurrency_tx are not numeric)', LOG_ERR);
189  return array();
190  }
191  }
192 
193  // initialize total (may be HT or TTC depending on price_base_type)
194  $tot_sans_remise = $pu * $qty * $progress / 100;
195  $tot_avec_remise_ligne = $tot_sans_remise * (1 - ((float) $remise_percent_ligne / 100));
196  $tot_avec_remise = $tot_avec_remise_ligne * (1 - ((float) $remise_percent_global / 100));
197 
198  // initialize result array
199  for ($i = 0; $i <= 15; $i++) {
200  $result[$i] = 0;
201  }
202 
203  // if there's some localtax including vat, we calculate localtaxes (we will add later)
204 
205  // if input unit price is 'HT', we need to have the totals with main VAT for a correct calculation
206  if ($price_base_type != 'TTC') {
207  $tot_sans_remise_wt = price2num($tot_sans_remise * (1 + ($txtva / 100)), 'MU');
208  $tot_avec_remise_wt = price2num($tot_avec_remise * (1 + ($txtva / 100)), 'MU');
209  $pu_wt = price2num($pu * (1 + ($txtva / 100)), 'MU');
210  } else {
211  $tot_sans_remise_wt = $tot_sans_remise;
212  $tot_avec_remise_wt = $tot_avec_remise;
213  $pu_wt = $pu;
214  }
215 
216  //print 'rr'.$price_base_type.'-'.$txtva.'-'.$tot_sans_remise_wt."-".$pu_wt."-".$uselocaltax1_rate."-".$localtax1_rate."-".$localtax1_type."\n";
217 
218  $localtaxes = array(0, 0, 0);
219  $apply_tax = false;
220  switch ($localtax1_type) {
221  case '2': // localtax on product or service
222  $apply_tax = true;
223  break;
224  case '4': // localtax on product
225  if ($type == 0) {
226  $apply_tax = true;
227  }
228  break;
229  case '6': // localtax on service
230  if ($type == 1) {
231  $apply_tax = true;
232  }
233  break;
234  }
235 
236  if ($uselocaltax1_rate && $apply_tax) {
237  $result[14] = price2num(($tot_sans_remise_wt * (1 + ($localtax1_rate / 100))) - $tot_sans_remise_wt, 'MT');
238  $localtaxes[0] += $result[14];
239 
240  $result[9] = price2num(($tot_avec_remise_wt * (1 + ($localtax1_rate / 100))) - $tot_avec_remise_wt, 'MT');
241  $localtaxes[1] += $result[9];
242 
243  $result[11] = price2num(($pu_wt * (1 + ($localtax1_rate / 100))) - $pu_wt, 'MU');
244  $localtaxes[2] += $result[11];
245  }
246 
247  $apply_tax = false;
248  switch ($localtax2_type) {
249  case '2': // localtax on product or service
250  $apply_tax = true;
251  break;
252  case '4': // localtax on product
253  if ($type == 0) {
254  $apply_tax = true;
255  }
256  break;
257  case '6': // localtax on service
258  if ($type == 1) {
259  $apply_tax = true;
260  }
261  break;
262  }
263  if ($uselocaltax2_rate && $apply_tax) {
264  $result[15] = price2num(($tot_sans_remise_wt * (1 + ($localtax2_rate / 100))) - $tot_sans_remise_wt, 'MT');
265  $localtaxes[0] += $result[15];
266 
267  $result[10] = price2num(($tot_avec_remise_wt * (1 + ($localtax2_rate / 100))) - $tot_avec_remise_wt, 'MT');
268  $localtaxes[1] += $result[10];
269 
270  $result[12] = price2num(($pu_wt * (1 + ($localtax2_rate / 100))) - $pu_wt, 'MU');
271  $localtaxes[2] += $result[12];
272  }
273 
274  //dol_syslog("price.lib::calcul_price_total $qty, $pu, $remise_percent_ligne, $txtva, $price_base_type $info_bits");
275  if ($price_base_type == 'HT') {
276  // We work to define prices using the price without tax
277  $result[6] = price2num($tot_sans_remise, 'MT');
278  $result[8] = price2num($tot_sans_remise * (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)) + $localtaxes[0], 'MT'); // Selon TVA NPR ou non
279  $result8bis = price2num($tot_sans_remise * (1 + ($txtva / 100)) + $localtaxes[0], 'MT'); // Si TVA consideree normale (non NPR)
280  $result[7] = price2num($result8bis - ($result[6] + $localtaxes[0]), 'MT');
281 
282  $result[0] = price2num($tot_avec_remise, 'MT');
283  $result[2] = price2num($tot_avec_remise * (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)) + $localtaxes[1], 'MT'); // Selon TVA NPR ou non
284  $result2bis = price2num($tot_avec_remise * (1 + ($txtva / 100)) + $localtaxes[1], 'MT'); // Si TVA consideree normale (non NPR)
285  $result[1] = price2num($result2bis - ($result[0] + $localtaxes[1]), 'MT'); // Total VAT = TTC - (HT + localtax)
286 
287  $result[3] = price2num($pu, 'MU');
288  $result[5] = price2num($pu * (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)) + $localtaxes[2], 'MU'); // Selon TVA NPR ou non
289  $result5bis = price2num($pu * (1 + ($txtva / 100)) + $localtaxes[2], 'MU'); // Si TVA consideree normale (non NPR)
290  $result[4] = price2num($result5bis - ($result[3] + $localtaxes[2]), 'MU');
291  } else {
292  // We work to define prices using the price with tax
293  $result[8] = price2num($tot_sans_remise + $localtaxes[0], 'MT');
294  $result[6] = price2num($tot_sans_remise / (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)), 'MT'); // Selon TVA NPR ou non
295  $result6bis = price2num($tot_sans_remise / (1 + ($txtva / 100)), 'MT'); // Si TVA consideree normale (non NPR)
296  $result[7] = price2num($result[8] - ($result6bis + $localtaxes[0]), 'MT');
297 
298  $result[2] = price2num($tot_avec_remise + $localtaxes[1], 'MT');
299  $result[0] = price2num($tot_avec_remise / (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)), 'MT'); // Selon TVA NPR ou non
300  $result0bis = price2num($tot_avec_remise / (1 + ($txtva / 100)), 'MT'); // Si TVA consideree normale (non NPR)
301  $result[1] = price2num($result[2] - ($result0bis + $localtaxes[1]), 'MT'); // Total VAT = TTC - (HT + localtax)
302 
303  $result[5] = price2num($pu + $localtaxes[2], 'MU');
304  $result[3] = price2num($pu / (1 + ((($info_bits & 1) ? 0 : $txtva) / 100)), 'MU'); // Selon TVA NPR ou non
305  $result3bis = price2num($pu / (1 + ($txtva / 100)), 'MU'); // Si TVA consideree normale (non NPR)
306  $result[4] = price2num($result[5] - ($result3bis + $localtaxes[2]), 'MU');
307  }
308 
309  // if there's some localtax without vat, we calculate localtaxes (we will add them at end)
310 
311  //If input unit price is 'TTC', we need to have the totals without main VAT for a correct calculation
312  if ($price_base_type == 'TTC') {
313  $tot_sans_remise = price2num($tot_sans_remise / (1 + ($txtva / 100)), 'MU');
314  $tot_avec_remise = price2num($tot_avec_remise / (1 + ($txtva / 100)), 'MU');
315  $pu = price2num($pu / (1 + ($txtva / 100)), 'MU');
316  }
317 
318  $apply_tax = false;
319  switch ($localtax1_type) {
320  case '1': // localtax on product or service
321  $apply_tax = true;
322  break;
323  case '3': // localtax on product
324  if ($type == 0) {
325  $apply_tax = true;
326  }
327  break;
328  case '5': // localtax on service
329  if ($type == 1) {
330  $apply_tax = true;
331  }
332  break;
333  }
334  if ($uselocaltax1_rate && $apply_tax) {
335  $result[14] = price2num(($tot_sans_remise * (1 + ($localtax1_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax1 for total_ht_without_discount
336  $result[8] += $result[14]; // total_ttc_without_discount + tax1
337 
338  $result[9] = price2num(($tot_avec_remise * (1 + ($localtax1_rate / 100))) - $tot_avec_remise, 'MT'); // amount tax1 for total_ht
339  $result[2] += $result[9]; // total_ttc + tax1
340 
341  $result[11] = price2num(($pu * (1 + ($localtax1_rate / 100))) - $pu, 'MU'); // amount tax1 for pu_ht
342  $result[5] += $result[11]; // pu_ht + tax1
343  }
344 
345  $apply_tax = false;
346  switch ($localtax2_type) {
347  case '1': // localtax on product or service
348  $apply_tax = true;
349  break;
350  case '3': // localtax on product
351  if ($type == 0) {
352  $apply_tax = true;
353  }
354  break;
355  case '5': // localtax on service
356  if ($type == 1) {
357  $apply_tax = true;
358  }
359  break;
360  }
361  if ($uselocaltax2_rate && $apply_tax) {
362  $result[15] = price2num(($tot_sans_remise * (1 + ($localtax2_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax2 for total_ht_without_discount
363  $result[8] += $result[15]; // total_ttc_without_discount + tax2
364 
365  $result[10] = price2num(($tot_avec_remise * (1 + ($localtax2_rate / 100))) - $tot_avec_remise, 'MT'); // amount tax2 for total_ht
366  $result[2] += $result[10]; // total_ttc + tax2
367 
368  $result[12] = price2num(($pu * (1 + ($localtax2_rate / 100))) - $pu, 'MU'); // amount tax2 for pu_ht
369  $result[5] += $result[12]; // pu_ht + tax2
370  }
371 
372  // If rounding is not using base 10 (rare)
373  if (!empty($conf->global->MAIN_ROUNDING_RULE_TOT)) {
374  if ($price_base_type == 'HT') {
375  $result[0] = price2num(round($result[0] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
376  $result[1] = price2num(round($result[1] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
377  $result[9] = price2num(round($result[9] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
378  $result[10] = price2num(round($result[10] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
379  $result[2] = price2num($result[0] + $result[1] + $result[9] + $result[10], 'MT');
380  } else {
381  $result[1] = price2num(round($result[1] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
382  $result[2] = price2num(round($result[2] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
383  $result[9] = price2num(round($result[9] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
384  $result[10] = price2num(round($result[10] / $conf->global->MAIN_ROUNDING_RULE_TOT, 0) * $conf->global->MAIN_ROUNDING_RULE_TOT, 'MT');
385  $result[0] = price2num($result[2] - $result[1] - $result[9] - $result[10], 'MT');
386  }
387  }
388 
389  // Multicurrency
390  if ($multicurrency_tx != 1) {
391  if ($multicurrency_code) {
392  $savMAIN_MAX_DECIMALS_UNIT = $conf->global->MAIN_MAX_DECIMALS_UNIT;
393  $savMAIN_MAX_DECIMALS_TOT = $conf->global->MAIN_MAX_DECIMALS_TOT;
394  $savMAIN_ROUNDING_RULE_TOT = $conf->global->MAIN_ROUNDING_RULE_TOT;
395 
396  // Set parameter for currency accurency according to the value of $multicurrency_code (this is because a foreign currency may have different rounding rules)
397  $keyforforeignMAIN_MAX_DECIMALS_UNIT = 'MAIN_MAX_DECIMALS_UNIT_'.$multicurrency_code;
398  $keyforforeignMAIN_MAX_DECIMALS_TOT = 'MAIN_MAX_DECIMALS_TOT_'.$multicurrency_code;
399  $keyforforeignMAIN_ROUNDING_RULE_TOT = 'MAIN_ROUNDING_RULE_TOT_'.$multicurrency_code;
400  if (!empty($conf->global->$keyforforeignMAIN_ROUNDING_RULE_TOT)) {
401  $conf->global->MAIN_MAX_DECIMALS_UNIT = $conf->global->$keyforforeignMAIN_MAX_DECIMALS_UNIT;
402  $conf->global->MAIN_MAX_DECIMALS_TOT = $conf->global->$keyforforeignMAIN_MAX_DECIMALS_TOT;
403  $conf->global->MAIN_ROUNDING_RULE_TOT = $conf->global->$keyforforeignMAIN_ROUNDING_RULE_TOT;
404  }
405  }
406 
407  // Recall function using the multicurrency price as reference price. We must set param $multicurrency_tx to 1 to avoid infinite loop.
408  $newresult = calcul_price_total($qty, $pu_devise, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller, $localtaxes_array, $progress, 1, 0, '');
409 
410  if ($multicurrency_code) {
411  // Restore setup of currency accurency
412  $conf->global->MAIN_MAX_DECIMALS_UNIT = $savMAIN_MAX_DECIMALS_UNIT;
413  $conf->global->MAIN_MAX_DECIMALS_TOT = $savMAIN_MAX_DECIMALS_TOT;
414  $conf->global->MAIN_ROUNDING_RULE_TOT = $savMAIN_ROUNDING_RULE_TOT;
415  }
416 
417  $result[16] = $newresult[0];
418  $result[17] = $newresult[1];
419  $result[18] = $newresult[2];
420  $result[19] = $newresult[3];
421  $result[20] = $newresult[4];
422  $result[21] = $newresult[5];
423  $result[22] = $newresult[6];
424  $result[23] = $newresult[7];
425  $result[24] = $newresult[8];
426  $result[25] = $newresult[9];
427  $result[26] = $newresult[10];
428  } else {
429  $result[16] = $result[0];
430  $result[17] = $result[1];
431  $result[18] = $result[2];
432  $result[19] = $result[3];
433  $result[20] = $result[4];
434  $result[21] = $result[5];
435  $result[22] = $result[6];
436  $result[23] = $result[7];
437  $result[24] = $result[8];
438  $result[25] = $result[9];
439  $result[26] = $result[10];
440  }
441 
442  //var_dump($result);
443  // initialize result array
444  //for ($i=0; $i <= 18; $i++) $result[$i] = (float) $result[$i];
445 
446  dol_syslog('Price.lib::calcul_price_total MAIN_ROUNDING_RULE_TOT='.(empty($conf->global->MAIN_ROUNDING_RULE_TOT) ? '' : $conf->global->MAIN_ROUNDING_RULE_TOT).' pu='.$pu.' qty='.$qty.' price_base_type='.$price_base_type.' total_ht='.$result[0].'-total_vat='.$result[1].'-total_ttc='.$result[2]);
447 
448  return $result;
449 }
Class to manage third parties objects (customers, suppliers, prospects...)
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
div float
Buy price without taxes.
Definition: style.css.php:913
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86