dolibarr  18.0.6
fournisseur.commande.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
5  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6  * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
8  * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
9  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10  * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12  * Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
13  * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
14  * Copyright (C) 2021 Josep Lluís Amador <joseplluis@lliuretic.cat>
15  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program. If not, see <https://www.gnu.org/licenses/>.
29  */
30 
37 require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
38 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
39 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
40 if (isModEnabled('productbatch')) {
41  require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
42 }
43 
44 
49 {
53  public $element = 'order_supplier';
54 
58  public $table_element = 'commande_fournisseur';
59 
63  public $table_element_line = 'commande_fournisseurdet';
64 
68  public $fk_element = 'fk_commande';
69 
73  public $picto = 'supplier_order';
74 
79  public $ismultientitymanaged = 1;
80 
85  public $restrictiononfksoc = 1;
86 
90  protected $table_ref_field = 'ref';
91 
95  public $id;
96 
101  public $ref;
102 
106  public $ref_supplier;
107 
113  public $ref_fourn;
114 
115  public $brouillon;
119  public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process runing -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
120  // -> 7=Canceled/Never received -> (reopen) 3=Process runing
121  // -> 6=Canceled -> (reopen) 2=Approved
122  // -> 9=Refused -> (reopen) 1=Validated
123  // Note: billed or not is on another field "billed"
124 
128  public $statuts;
129 
133  public $statuts_short;
134 
135  public $billed;
136 
137  public $socid;
138  public $fourn_id;
139  public $date;
140  public $date_creation;
141  public $date_valid;
142  public $date_approve;
143  public $date_approve2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
144  public $date_commande;
145 
150  public $date_livraison;
151 
155  public $delivery_date;
156 
157  public $total_ht;
158  public $total_tva;
159  public $total_localtax1; // Total Local tax 1
160  public $total_localtax2; // Total Local tax 2
161  public $total_ttc;
162  public $source;
163 
167  public $fk_project;
168 
169  public $cond_reglement_id;
170  public $cond_reglement_code;
171  public $cond_reglement_label; // Label
172  public $cond_reglement_doc; // Label on documents
173 
177  public $fk_account;
178 
182  public $mode_reglement_id;
183 
187  public $mode_reglement_code;
188 
192  public $mode_reglement;
193  public $user_author_id;
194  public $user_valid_id;
195  public $user_approve_id;
196  public $user_approve_id2; // Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
197 
198  public $refuse_note;
199 
200  public $extraparams = array();
201 
205  public $lines = array();
206 
210  public $line;
211 
212  // Add for supplier_proposal
213  public $origin;
214  public $origin_id;
215  public $linked_objects = array();
216 
217  // Multicurrency
221  public $fk_multicurrency;
222 
223  public $multicurrency_code;
224  public $multicurrency_tx;
225  public $multicurrency_total_ht;
226  public $multicurrency_total_tva;
227  public $multicurrency_total_ttc;
228 
229 
257  public $fields = array(
258  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>10),
259  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>25, 'searchall'=>1),
260  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>35),
261  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefOrderSupplierShort', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'searchall'=>1),
262  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>45),
263  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>710),
264  'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>720),
265  'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>725),
266  'date_commande' =>array('type'=>'date', 'label'=>'OrderDateShort', 'enabled'=>1, 'visible'=>1, 'position'=>70),
267  'date_livraison' =>array('type'=>'datetime', 'label'=>'DeliveryDate', 'enabled'=>'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible'=>1, 'position'=>74),
268  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>41),
269  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>3, 'notnull'=>-1, 'position'=>80),
270  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>711),
271  'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>721),
272  'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>726),
273  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>100),
274  'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>710),
275  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
276  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>1, 'position'=>135, 'isameasure'=>1),
277  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LT1', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
278  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LT2', 'enabled'=>1, 'visible'=>3, 'position'=>145, 'isameasure'=>1),
279  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
280  'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>750, 'searchall'=>1),
281  'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>760, 'searchall'=>1),
282  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>165),
283  'fk_input_method' =>array('type'=>'integer', 'label'=>'OrderMode', 'enabled'=>1, 'visible'=>3, 'position'=>170),
284  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>3, 'position'=>175),
285  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>3, 'position'=>180),
286  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>0, 'position'=>190),
287  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>3, 'position'=>200),
288  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>3, 'position'=>205),
289  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>3, 'position'=>210),
290  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>0, 'position'=>215),
291  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Currency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>220),
292  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'CurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>225),
293  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>230),
294  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>235),
295  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
296  'date_creation' =>array('type'=>'datetime', 'label'=>'Date creation', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
297  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>1, 'notnull'=>1, 'position'=>50),
298  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1000, 'index'=>1),
299  'tms'=>array('type'=>'datetime', 'label'=>"DateModificationShort", 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
300  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'position'=>700),
301  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>701),
302  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>0, 'position'=>900),
303  );
304 
305 
309  const STATUS_DRAFT = 0;
310 
314  const STATUS_VALIDATED = 1;
315 
319  const STATUS_ACCEPTED = 2;
320 
324  const STATUS_ORDERSENT = 3;
325 
330 
335 
339  const STATUS_CANCELED = 6;
340 
345 
349  const STATUS_REFUSED = 9;
350 
351 
356 
357 
358 
364  public function __construct($db)
365  {
366  $this->db = $db;
367  }
368 
369 
377  public function fetch($id, $ref = '')
378  {
379  global $conf;
380 
381  // Check parameters
382  if (empty($id) && empty($ref)) {
383  return -1;
384  }
385 
386  $sql = "SELECT c.rowid, c.entity, c.ref, ref_supplier, c.fk_soc, c.fk_statut, c.amount_ht, c.total_ht, c.total_ttc, c.total_tva,";
387  $sql .= " c.localtax1, c.localtax2, ";
388  $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
389  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_approve, c.fk_user_approve2,";
390  $sql .= " c.date_commande as date_commande, c.date_livraison as delivery_date, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_projet as fk_project, c.remise_percent, c.source, c.fk_input_method,";
391  $sql .= " c.fk_account,";
392  $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
393  $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
394  $sql .= " cm.libelle as methode_commande,";
395  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
396  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
397  $sql .= ', c.fk_incoterms, c.location_incoterms';
398  $sql .= ', i.libelle as label_incoterms';
399  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
400  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
401  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON c.fk_mode_reglement = p.id";
402  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_input_method as cm ON cm.rowid = c.fk_input_method";
403  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
404 
405  if (empty($id)) {
406  $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
407  } else {
408  $sql .= " WHERE c.rowid=".((int) $id);
409  }
410 
411  if ($ref) {
412  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
413  }
414 
415  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
416  $resql = $this->db->query($sql);
417  if ($resql) {
418  $obj = $this->db->fetch_object($resql);
419  if (!$obj) {
420  $this->error = 'Bill with id '.$id.' not found';
421  dol_syslog(get_class($this).'::fetch '.$this->error);
422  return 0;
423  }
424 
425  $this->id = $obj->rowid;
426  $this->entity = $obj->entity;
427 
428  $this->ref = $obj->ref;
429  $this->ref_supplier = $obj->ref_supplier;
430  $this->socid = $obj->fk_soc;
431  $this->fourn_id = $obj->fk_soc;
432  $this->statut = $obj->fk_statut;
433  $this->status = $obj->fk_statut;
434  $this->billed = $obj->billed;
435  $this->user_author_id = $obj->fk_user_author;
436  $this->user_valid_id = $obj->fk_user_valid;
437  $this->user_approve_id = $obj->fk_user_approve;
438  $this->user_approve_id2 = $obj->fk_user_approve2;
439  $this->total_ht = $obj->total_ht;
440  $this->total_tva = $obj->total_tva;
441  $this->total_localtax1 = $obj->localtax1;
442  $this->total_localtax2 = $obj->localtax2;
443  $this->total_ttc = $obj->total_ttc;
444  $this->date_creation = $this->db->jdate($obj->date_creation);
445  $this->date_valid = $this->db->jdate($obj->date_valid);
446  $this->date_approve = $this->db->jdate($obj->date_approve);
447  $this->date_approve2 = $this->db->jdate($obj->date_approve2);
448  $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
449  if (isset($obj->date_commande)) {
450  $this->date = $this->date_commande;
451  } else {
452  $this->date = $this->date_creation;
453  }
454  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
455  $this->delivery_date = $this->db->jdate($obj->delivery_date);
456  $this->remise_percent = $obj->remise_percent;
457  $this->methode_commande_id = $obj->fk_input_method;
458  $this->methode_commande = $obj->methode_commande;
459 
460  $this->source = $obj->source;
461  $this->fk_project = $obj->fk_project;
462  $this->cond_reglement_id = $obj->fk_cond_reglement;
463  $this->cond_reglement_code = $obj->cond_reglement_code;
464  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
465  $this->cond_reglement_label = $obj->cond_reglement_label;
466  $this->cond_reglement_doc = $obj->cond_reglement_doc;
467  $this->fk_account = $obj->fk_account;
468  $this->mode_reglement_id = $obj->fk_mode_reglement;
469  $this->mode_reglement_code = $obj->mode_reglement_code;
470  $this->mode_reglement = $obj->mode_reglement_libelle;
471  $this->note = $obj->note_private; // deprecated
472  $this->note_private = $obj->note_private;
473  $this->note_public = $obj->note_public;
474  $this->model_pdf = $obj->model_pdf;
475  $this->modelpdf = $obj->model_pdf; // deprecated
476 
477  //Incoterms
478  $this->fk_incoterms = $obj->fk_incoterms;
479  $this->location_incoterms = $obj->location_incoterms;
480  $this->label_incoterms = $obj->label_incoterms;
481 
482  // Multicurrency
483  $this->fk_multicurrency = $obj->fk_multicurrency;
484  $this->multicurrency_code = $obj->multicurrency_code;
485  $this->multicurrency_tx = $obj->multicurrency_tx;
486  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
487  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
488  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
489 
490  $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
491 
492  $this->db->free($resql);
493 
494  // Retrieve all extrafield
495  // fetch optionals attributes and labels
496  $this->fetch_optionals();
497 
498  if ($this->statut == 0) {
499  $this->brouillon = 1;
500  }
501 
502  /*
503  * Lines
504  */
505  $result = $this->fetch_lines();
506 
507  if ($result < 0) {
508  return -1;
509  } else {
510  return 1;
511  }
512  } else {
513  $this->error = $this->db->error()." sql=".$sql;
514  return -1;
515  }
516  }
517 
518  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
525  public function fetch_lines($only_product = 0)
526  {
527  global $conf;
528  // phpcs:enable
529 
530  $this->lines = array();
531 
532  $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
533  $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
534  $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
535  $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.special_code, l.fk_parent_line, l.rang,";
536  $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch, p.barcode as product_barcode,";
537  $sql .= " l.fk_unit,";
538  $sql .= " l.date_start, l.date_end,";
539  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
540  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
541  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
542  $sql .= " WHERE l.fk_commande = ".((int) $this->id);
543  if ($only_product) {
544  $sql .= ' AND p.fk_product_type = 0';
545  }
546  $sql .= " ORDER BY l.rang, l.rowid";
547  //print $sql;
548 
549  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
550 
551  $result = $this->db->query($sql);
552  if ($result) {
553  $num = $this->db->num_rows($result);
554  $i = 0;
555 
556  while ($i < $num) {
557  $objp = $this->db->fetch_object($result);
558 
559  $line = new CommandeFournisseurLigne($this->db);
560 
561  $line->id = $objp->rowid;
562  $line->fk_commande = $objp->fk_commande;
563  $line->desc = $objp->description;
564  $line->description = $objp->description;
565  $line->qty = $objp->qty;
566  $line->tva_tx = $objp->tva_tx;
567  $line->localtax1_tx = $objp->localtax1_tx;
568  $line->localtax2_tx = $objp->localtax2_tx;
569  $line->localtax1_type = $objp->localtax1_type;
570  $line->localtax2_type = $objp->localtax2_type;
571  $line->subprice = $objp->subprice;
572  $line->pu_ht = $objp->subprice;
573  $line->remise_percent = $objp->remise_percent;
574 
575  $line->vat_src_code = $objp->vat_src_code;
576  $line->total_ht = $objp->total_ht;
577  $line->total_tva = $objp->total_tva;
578  $line->total_localtax1 = $objp->total_localtax1;
579  $line->total_localtax2 = $objp->total_localtax2;
580  $line->total_ttc = $objp->total_ttc;
581  $line->product_type = $objp->product_type;
582 
583  $line->fk_product = $objp->fk_product;
584 
585  $line->libelle = $objp->product_label; // deprecated
586  $line->product_label = $objp->product_label;
587  $line->product_desc = $objp->product_desc;
588  $line->product_tobatch = $objp->product_tobatch;
589  $line->product_barcode = $objp->product_barcode;
590 
591  $line->ref = $objp->product_ref; // Ref of product
592  $line->product_ref = $objp->product_ref; // Ref of product
593  $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
594  $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
595 
596  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
597  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
598  // Move this into another method and call it when required.
599 
600  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
601  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
602  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
603  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
604  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
605  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
606  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
607  $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
608  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
609  $sqlsearchpackage .= " LIMIT 1";
610 
611  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
612  if ($resqlsearchpackage) {
613  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
614  if ($objsearchpackage) {
615  $line->fk_fournprice = $objsearchpackage->rowid;
616  $line->packaging = $objsearchpackage->packaging;
617  }
618  } else {
619  $this->error = $this->db->lasterror();
620  return -1;
621  }
622  }
623 
624  $line->date_start = $this->db->jdate($objp->date_start);
625  $line->date_end = $this->db->jdate($objp->date_end);
626  $line->fk_unit = $objp->fk_unit;
627 
628  // Multicurrency
629  $line->fk_multicurrency = $objp->fk_multicurrency;
630  $line->multicurrency_code = $objp->multicurrency_code;
631  $line->multicurrency_subprice = $objp->multicurrency_subprice;
632  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
633  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
634  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
635 
636  $line->special_code = $objp->special_code;
637  $line->fk_parent_line = $objp->fk_parent_line;
638 
639  $line->rang = $objp->rang;
640 
641  // Retrieve all extrafield
642  // fetch optionals attributes and labels
643  $line->fetch_optionals();
644 
645  $this->lines[$i] = $line;
646 
647  $i++;
648  }
649  $this->db->free($result);
650 
651  return $num;
652  } else {
653  $this->error = $this->db->error()." sql=".$sql;
654  return -1;
655  }
656  }
657 
666  public function valid($user, $idwarehouse = 0, $notrigger = 0)
667  {
668  global $langs, $conf;
669  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
670 
671  $error = 0;
672 
673  dol_syslog(get_class($this)."::valid");
674  $result = 0;
675  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
676  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
677  $this->db->begin();
678 
679  // Definition of supplier order numbering model name
680  $soc = new Societe($this->db);
681  $soc->fetch($this->fourn_id);
682 
683  // Check if object has a temporary ref
684  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
685  $num = $this->getNextNumRef($soc);
686  } else {
687  $num = $this->ref;
688  }
689  $this->newref = dol_sanitizeFileName($num);
690 
691  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
692  $sql .= " SET ref='".$this->db->escape($num)."',";
693  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
694  $sql .= " date_valid='".$this->db->idate(dol_now())."',";
695  $sql .= " fk_user_valid = ".((int) $user->id);
696  $sql .= " WHERE rowid = ".((int) $this->id);
697  $sql .= " AND fk_statut = ".self::STATUS_DRAFT;
698 
699  $resql = $this->db->query($sql);
700  if (!$resql) {
701  dol_print_error($this->db);
702  $error++;
703  }
704 
705  if (!$error && !$notrigger) {
706  // Call trigger
707  $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
708  if ($result < 0) {
709  $error++;
710  }
711  // End call triggers
712  }
713 
714  if (!$error) {
715  $this->oldref = $this->ref;
716 
717  // Rename directory if dir was a temporary ref
718  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
719  // Now we rename also files into index
720  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
721  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
722  $resql = $this->db->query($sql);
723  if (!$resql) {
724  $error++; $this->error = $this->db->lasterror();
725  }
726  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
727  $sql .= " WHERE filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
728  $resql = $this->db->query($sql);
729  if (!$resql) {
730  $error++; $this->error = $this->db->lasterror();
731  }
732 
733  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
734  $oldref = dol_sanitizeFileName($this->ref);
735  $newref = dol_sanitizeFileName($num);
736  $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
737  $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
738  if (!$error && file_exists($dirsource)) {
739  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
740 
741  if (@rename($dirsource, $dirdest)) {
742  dol_syslog("Rename ok");
743  // Rename docs starting with $oldref with $newref
744  $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
745  foreach ($listoffiles as $fileentry) {
746  $dirsource = $fileentry['name'];
747  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
748  $dirsource = $fileentry['path'].'/'.$dirsource;
749  $dirdest = $fileentry['path'].'/'.$dirdest;
750  @rename($dirsource, $dirdest);
751  }
752  }
753  }
754  }
755  }
756 
757  if (!$error) {
758  $result = 1;
759  $this->statut = self::STATUS_VALIDATED;
760  $this->ref = $num;
761  }
762 
763  if (!$error) {
764  $this->db->commit();
765  return 1;
766  } else {
767  $this->db->rollback();
768  return -1;
769  }
770  } else {
771  $this->error = 'NotAuthorized';
772  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
773  return -1;
774  }
775  }
776 
783  public function getLibStatut($mode = 0)
784  {
785  return $this->LibStatut($this->statut, $mode, $this->billed);
786  }
787 
788  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
797  public function LibStatut($status, $mode = 0, $billed = 0)
798  {
799  // phpcs:enable
800  global $conf, $langs, $hookmanager;
801 
802  if (empty($this->statuts) || empty($this->statuts_short)) {
803  $langs->load('orders');
804 
805  $this->statuts[0] = 'StatusSupplierOrderDraft';
806  $this->statuts[1] = 'StatusSupplierOrderValidated';
807  $this->statuts[2] = 'StatusSupplierOrderApproved';
808  if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
809  $this->statuts[3] = 'StatusSupplierOrderOnProcess';
810  } else {
811  $this->statuts[3] = 'StatusSupplierOrderOnProcessWithValidation';
812  }
813  $this->statuts[4] = 'StatusSupplierOrderReceivedPartially';
814  $this->statuts[5] = 'StatusSupplierOrderReceivedAll';
815  $this->statuts[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
816  $this->statuts[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
817  $this->statuts[9] = 'StatusSupplierOrderRefused';
818 
819  // List of language codes for status
820  $this->statuts_short[0] = 'StatusSupplierOrderDraftShort';
821  $this->statuts_short[1] = 'StatusSupplierOrderValidatedShort';
822  $this->statuts_short[2] = 'StatusSupplierOrderApprovedShort';
823  $this->statuts_short[3] = 'StatusSupplierOrderOnProcessShort';
824  $this->statuts_short[4] = 'StatusSupplierOrderReceivedPartiallyShort';
825  $this->statuts_short[5] = 'StatusSupplierOrderReceivedAllShort';
826  $this->statuts_short[6] = 'StatusSupplierOrderCanceledShort';
827  $this->statuts_short[7] = 'StatusSupplierOrderCanceledShort';
828  $this->statuts_short[9] = 'StatusSupplierOrderRefusedShort';
829  }
830 
831  $statustrans = array(
832  0 => 'status0',
833  1 => 'status1b',
834  2 => 'status1',
835  3 => 'status4',
836  4 => 'status4b',
837  5 => 'status6',
838  6 => 'status9',
839  7 => 'status9',
840  9 => 'status9',
841  );
842 
843  $statusClass = 'status0';
844  if (!empty($statustrans[$status])) {
845  $statusClass = $statustrans[$status];
846  }
847 
848  $billedtext = '';
849  if ($billed) {
850  $billedtext = ' - '.$langs->trans("Billed");
851  }
852  if ($status == 5 && $billed) {
853  $statusClass = 'status6';
854  }
855 
856  $statusLong = $langs->transnoentitiesnoconv($this->statuts[$status]).$billedtext;
857  $statusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
858 
859  $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
860  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
861  if ($reshook > 0) {
862  return $hookmanager->resPrint;
863  }
864 
865  return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
866  }
867 
875  public function getTooltipContentArray($params)
876  {
877  global $conf, $langs, $user;
878 
879  $langs->loadLangs(['bills', 'orders']);
880 
881  $datas = [];
882  $nofetch = !empty($params['nofetch']);
883 
884  if ($user->hasRight("fournisseur", "commande", "read")) {
885  $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
886  if (isset($this->statut)) {
887  $datas['picto'] .= ' '.$this->getLibStatut(5);
888  }
889  if (!empty($this->ref)) {
890  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
891  }
892  if (!empty($this->ref_supplier)) {
893  $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
894  }
895  if (!$nofetch) {
896  $langs->load('companies');
897  if (empty($this->thirdparty)) {
898  $this->fetch_thirdparty();
899  }
900  $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
901  }
902  if (!empty($this->total_ht)) {
903  $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
904  }
905  if (!empty($this->total_tva)) {
906  $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
907  }
908  if (!empty($this->total_ttc)) {
909  $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
910  }
911  if (!empty($this->date)) {
912  $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
913  }
914  if (!empty($this->delivery_date)) {
915  $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
916  }
917  }
918  return $datas;
919  }
920 
931  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
932  {
933  global $langs, $conf, $user, $hookmanager;
934 
935  $result = '';
936  $params = [
937  'id' => $this->id,
938  'objecttype' => $this->element,
939  'option' => $option,
940  'nofetch' => 1
941  ];
942  $classfortooltip = 'classfortooltip';
943  $dataparams = '';
944  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
945  $classfortooltip = 'classforajaxtooltip';
946  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
947  $label = '';
948  } else {
949  $label = implode($this->getTooltipContentArray($params));
950  }
951 
952  $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
953 
954  if ($option !== 'nolink') {
955  // Add param to save lastsearch_values or not
956  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
957  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
958  $add_save_lastsearch_values = 1;
959  }
960  if ($add_save_lastsearch_values) {
961  $url .= '&save_lastsearch_values=1';
962  }
963  }
964 
965  $linkclose = '';
966  if (empty($notooltip)) {
967  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
968  $label = $langs->trans("ShowOrder");
969  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
970  }
971  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
972  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
973  }
974 
975  $linkstart = '<a href="'.$url.'"';
976  $linkstart .= $linkclose.'>';
977  $linkend = '</a>';
978 
979  $result .= $linkstart;
980  if ($withpicto) {
981  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
982  }
983  if ($withpicto != 2) {
984  $result .= $this->ref;
985  }
986  $result .= $linkend;
987 
988  if ($addlinktonotes) {
989  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
990  if ($txttoshow) {
991  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
992  $result .= ' <span class="note inline-block">';
993  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
994  $result .= img_picto('', 'note');
995  $result .= '</a>';
996  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
997  //$result.='</a>';
998  $result .= '</span>';
999  }
1000  }
1001 
1002  global $action;
1003  $hookmanager->initHooks(array($this->element . 'dao'));
1004  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1005  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1006  if ($reshook > 0) {
1007  $result = $hookmanager->resPrint;
1008  } else {
1009  $result .= $hookmanager->resPrint;
1010  }
1011  return $result;
1012  }
1013 
1014 
1022  public function getNextNumRef($soc)
1023  {
1024  global $db, $langs, $conf;
1025  $langs->load("orders");
1026 
1027  if (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER)) {
1028  $mybool = false;
1029 
1030  $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1031  $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1032 
1033  // Include file with class
1034  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1035 
1036  foreach ($dirmodels as $reldir) {
1037  $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1038 
1039  // Load file with numbering class (if found)
1040  $mybool |= @include_once $dir.$file;
1041  }
1042 
1043  if ($mybool === false) {
1044  dol_print_error('', "Failed to include file ".$file);
1045  return '';
1046  }
1047 
1048  $obj = new $classname();
1049  $numref = $obj->getNextValue($soc, $this);
1050 
1051  if ($numref != "") {
1052  return $numref;
1053  } else {
1054  $this->error = $obj->error;
1055  return -1;
1056  }
1057  } else {
1058  $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1059  return -2;
1060  }
1061  }
1068  public function classifyBilled(User $user)
1069  {
1070  $error = 0;
1071 
1072  if ($this->billed) {
1073  return 0;
1074  }
1075 
1076  $this->db->begin();
1077 
1078  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
1079  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1080 
1081  if ($this->db->query($sql)) {
1082  if (!$error) {
1083  // Call trigger
1084  $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1085  if ($result < 0) {
1086  $error++;
1087  }
1088  // End call triggers
1089  }
1090 
1091  if (!$error) {
1092  $this->billed = 1;
1093 
1094  $this->db->commit();
1095  return 1;
1096  } else {
1097  $this->db->rollback();
1098  return -1;
1099  }
1100  } else {
1101  dol_print_error($this->db);
1102 
1103  $this->db->rollback();
1104  return -1;
1105  }
1106  }
1107 
1116  public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1117  {
1118  global $langs, $conf;
1119  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1120 
1121  $error = 0;
1122 
1123  dol_syslog(get_class($this)."::approve");
1124 
1125  if ($user->hasRight("fournisseur", "commande", "approuver")) {
1126  $now = dol_now();
1127 
1128  $this->db->begin();
1129 
1130  // Definition of order numbering model name
1131  $soc = new Societe($this->db);
1132  $soc->fetch($this->fourn_id);
1133 
1134  // Check if object has a temporary ref
1135  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1136  $num = $this->getNextNumRef($soc);
1137  } else {
1138  $num = $this->ref;
1139  }
1140  $this->newref = dol_sanitizeFileName($num);
1141 
1142  // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1143  $movetoapprovestatus = true;
1144  $comment = '';
1145 
1146  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1147  $sql .= " SET ref='".$this->db->escape($num)."',";
1148  if (empty($secondlevel)) { // standard or first level approval
1149  $sql .= " date_approve='".$this->db->idate($now)."',";
1150  $sql .= " fk_user_approve = ".$user->id;
1151  if (!empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1152  if (empty($this->user_approve_id2)) {
1153  $movetoapprovestatus = false; // second level approval not done
1154  $comment = ' (first level)';
1155  }
1156  }
1157  } else // request a second level approval
1158  {
1159  $sql .= " date_approve2='".$this->db->idate($now)."',";
1160  $sql .= " fk_user_approve2 = ".((int) $user->id);
1161  if (empty($this->user_approve_id)) {
1162  $movetoapprovestatus = false; // first level approval not done
1163  }
1164  $comment = ' (second level)';
1165  }
1166  // If double approval is required and first approval, we keep status to 1 = validated
1167  if ($movetoapprovestatus) {
1168  $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1169  } else {
1170  $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1171  }
1172  $sql .= " WHERE rowid = ".((int) $this->id);
1173  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1174 
1175  if ($this->db->query($sql)) {
1176  if (!empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT)) {
1177  $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1178  if ($result < 0 && $result != -2) { // -2 means already exists
1179  $error++;
1180  }
1181  }
1182 
1183  // If stock is incremented on validate order, we must increment it
1184  if (!$error && $movetoapprovestatus && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) {
1185  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1186  $langs->load("agenda");
1187 
1188  $cpt = count($this->lines);
1189  for ($i = 0; $i < $cpt; $i++) {
1190  // Product with reference
1191  if ($this->lines[$i]->fk_product > 0) {
1192  $this->line = $this->lines[$i];
1193  $mouvP = new MouvementStock($this->db);
1194  $mouvP->origin = &$this;
1195  $mouvP->setOrigin($this->element, $this->id);
1196  // We decrement stock of product (and sub-products)
1197  $up_ht_disc = $this->lines[$i]->subprice;
1198  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1199  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1200  }
1201  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1202  if ($result < 0) {
1203  $error++;
1204  }
1205  unset($this->line);
1206  }
1207  }
1208  }
1209 
1210  if (!$error) {
1211  // Call trigger
1212  $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1213  if ($result < 0) {
1214  $error++;
1215  }
1216  // End call triggers
1217  }
1218 
1219  if (!$error) {
1220  $this->ref = $this->newref;
1221 
1222  if ($movetoapprovestatus) {
1223  $this->statut = self::STATUS_ACCEPTED;
1224  } else {
1225  $this->statut = self::STATUS_VALIDATED;
1226  }
1227  if (empty($secondlevel)) { // standard or first level approval
1228  $this->date_approve = $now;
1229  $this->user_approve_id = $user->id;
1230  } else // request a second level approval
1231  {
1232  $this->date_approve2 = $now;
1233  $this->user_approve_id2 = $user->id;
1234  }
1235 
1236  $this->db->commit();
1237  return 1;
1238  } else {
1239  $this->db->rollback();
1240  return -1;
1241  }
1242  } else {
1243  $this->db->rollback();
1244  $this->error = $this->db->lasterror();
1245  return -1;
1246  }
1247  } else {
1248  dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1249  }
1250  return -1;
1251  }
1252 
1259  public function refuse($user)
1260  {
1261  global $conf, $langs;
1262 
1263  $error = 0;
1264 
1265  dol_syslog(get_class($this)."::refuse");
1266  $result = 0;
1267  if ($user->hasRight("fournisseur", "commande", "approuver")) {
1268  $this->db->begin();
1269 
1270  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1271  $sql .= " WHERE rowid = ".((int) $this->id);
1272 
1273  if ($this->db->query($sql)) {
1274  $result = 0;
1275 
1276  if ($error == 0) {
1277  // Call trigger
1278  $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1279  if ($result < 0) {
1280  $error++;
1281  $this->db->rollback();
1282  } else {
1283  $this->db->commit();
1284  }
1285  // End call triggers
1286  }
1287  } else {
1288  $this->db->rollback();
1289  $this->error = $this->db->lasterror();
1290  dol_syslog(get_class($this)."::refuse Error -1");
1291  $result = -1;
1292  }
1293  } else {
1294  dol_syslog(get_class($this)."::refuse Not Authorized");
1295  }
1296  return $result;
1297  }
1298 
1299  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1308  public function Cancel($user, $idwarehouse = -1)
1309  {
1310  // phpcs:enable
1311  global $langs, $conf;
1312 
1313  $error = 0;
1314 
1315  //dol_syslog("CommandeFournisseur::Cancel");
1316  $result = 0;
1317  if ($user->hasRight("fournisseur", "commande", "commander")) {
1318  $statut = self::STATUS_CANCELED;
1319 
1320  $this->db->begin();
1321 
1322  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".((int) $statut);
1323  $sql .= " WHERE rowid = ".((int) $this->id);
1324  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1325  if ($this->db->query($sql)) {
1326  $result = 0;
1327 
1328  // Call trigger
1329  $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1330  if ($result < 0) {
1331  $error++;
1332  }
1333  // End call triggers
1334 
1335  if ($error == 0) {
1336  $this->db->commit();
1337  return 1;
1338  } else {
1339  $this->db->rollback();
1340  return -1;
1341  }
1342  } else {
1343  $this->db->rollback();
1344  $this->error = $this->db->lasterror();
1345  dol_syslog(get_class($this)."::cancel ".$this->error);
1346  return -1;
1347  }
1348  } else {
1349  dol_syslog(get_class($this)."::cancel Not Authorized");
1350  return -1;
1351  }
1352  }
1353 
1363  public function commande($user, $date, $methode, $comment = '')
1364  {
1365  global $langs;
1366  dol_syslog(get_class($this)."::commande");
1367  $error = 0;
1368  if ($user->hasRight("fournisseur", "commande", "commander")) {
1369  $this->db->begin();
1370 
1371  $newnoteprivate = $this->note_private;
1372  if ($comment) {
1373  $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1374  }
1375 
1376  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1377  $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1378  $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1379  $sql .= " WHERE rowid=".((int) $this->id);
1380 
1381  dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1382  if ($this->db->query($sql)) {
1383  $this->statut = self::STATUS_ORDERSENT;
1384  $this->methode_commande_id = $methode;
1385  $this->date_commande = $date;
1386  $this->context = array('comments' => $comment);
1387 
1388  // Call trigger
1389  $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1390  if ($result < 0) {
1391  $error++;
1392  }
1393  // End call triggers
1394  } else {
1395  $error++;
1396  $this->error = $this->db->lasterror();
1397  $this->errors[] = $this->db->lasterror();
1398  }
1399 
1400  if (!$error) {
1401  $this->db->commit();
1402  } else {
1403  $this->db->rollback();
1404  }
1405  } else {
1406  $error++;
1407  $this->error = $langs->trans('NotAuthorized');
1408  $this->errors[] = $langs->trans('NotAuthorized');
1409  dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1410  }
1411 
1412  return ($error ? -1 : 1);
1413  }
1414 
1422  public function create($user, $notrigger = 0)
1423  {
1424  global $langs, $conf, $hookmanager;
1425 
1426  $this->db->begin();
1427 
1428  $error = 0;
1429  $now = dol_now();
1430 
1431  // set tmp vars
1432  $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1433  if (empty($date)) {
1434  $date = $now;
1435  }
1436  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1437 
1438  // Clean parameters
1439  if (empty($this->source)) {
1440  $this->source = 0;
1441  }
1442 
1443  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1444  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1445  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1446  } else {
1447  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1448  }
1449  if (empty($this->fk_multicurrency)) {
1450  $this->multicurrency_code = $conf->currency;
1451  $this->fk_multicurrency = 0;
1452  $this->multicurrency_tx = 1;
1453  }
1454 
1455  // We set order into draft status
1456  $this->brouillon = 1;
1457 
1458  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1459  $sql .= "ref";
1460  $sql .= ", ref_supplier";
1461  $sql .= ", note_private";
1462  $sql .= ", note_public";
1463  $sql .= ", entity";
1464  $sql .= ", fk_soc";
1465  $sql .= ", fk_projet";
1466  $sql .= ", date_creation";
1467  $sql .= ", date_livraison";
1468  $sql .= ", fk_user_author";
1469  $sql .= ", fk_statut";
1470  $sql .= ", source";
1471  $sql .= ", model_pdf";
1472  $sql .= ", fk_mode_reglement";
1473  $sql .= ", fk_cond_reglement";
1474  $sql .= ", fk_account";
1475  $sql .= ", fk_incoterms, location_incoterms";
1476  $sql .= ", fk_multicurrency";
1477  $sql .= ", multicurrency_code";
1478  $sql .= ", multicurrency_tx";
1479  $sql .= ") ";
1480  $sql .= " VALUES (";
1481  $sql .= "'(PROV)'";
1482  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
1483  $sql .= ", '".$this->db->escape($this->note_private)."'";
1484  $sql .= ", '".$this->db->escape($this->note_public)."'";
1485  $sql .= ", ".setEntity($this);
1486  $sql .= ", ".((int) $this->socid);
1487  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1488  $sql .= ", '".$this->db->idate($date)."'";
1489  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1490  $sql .= ", ".((int) $user->id);
1491  $sql .= ", ".self::STATUS_DRAFT;
1492  $sql .= ", ".((int) $this->source);
1493  $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1494  $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1495  $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1496  $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1497  $sql .= ", ".(int) $this->fk_incoterms;
1498  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1499  $sql .= ", ".(int) $this->fk_multicurrency;
1500  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1501  $sql .= ", ".(double) $this->multicurrency_tx;
1502  $sql .= ")";
1503 
1504  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1505  if ($this->db->query($sql)) {
1506  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1507 
1508  if ($this->id) {
1509  $num = count($this->lines);
1510 
1511  // insert products details into database
1512  for ($i = 0; $i < $num; $i++) {
1513  $line = $this->lines[$i];
1514  if (!is_object($line)) {
1515  $line = (object) $line;
1516  }
1517 
1518 
1519  //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1520 
1521  // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1522  $result = $this->addline(
1523  $line->desc,
1524  $line->subprice,
1525  $line->qty,
1526  $line->tva_tx,
1527  $line->localtax1_tx,
1528  $line->localtax2_tx,
1529  $line->fk_product,
1530  0,
1531  $line->ref_fourn, // $line->ref_fourn comes from field ref into table of lines. Value may ba a ref that does not exists anymore, so we first try with value of product
1532  $line->remise_percent,
1533  'HT',
1534  0,
1535  $line->product_type,
1536  $line->info_bits,
1537  false,
1538  $line->date_start,
1539  $line->date_end,
1540  $line->array_options,
1541  $line->fk_unit,
1542  $line->multicurrency_subprice, // pu_ht_devise
1543  $line->origin, // origin
1544  $line->origin_id, // origin_id
1545  $line->rang, // rang
1546  $line->special_code
1547  );
1548  if ($result < 0) {
1549  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functionnal error
1550  $this->db->rollback();
1551  return -1;
1552  }
1553  }
1554 
1555  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1556  $sql .= " SET ref='(PROV".$this->id.")'";
1557  $sql .= " WHERE rowid=".((int) $this->id);
1558  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1559  if ($this->db->query($sql)) {
1560  // Add link with price request and supplier order
1561  if ($this->id) {
1562  $this->ref = "(PROV".$this->id.")";
1563 
1564  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1565  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1566  }
1567 
1568  // Add object linked
1569  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1570  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1571  if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
1572  foreach ($tmp_origin_id as $origin_id) {
1573  $ret = $this->add_object_linked($origin, $origin_id);
1574  if (!$ret) {
1575  dol_print_error($this->db);
1576  $error++;
1577  }
1578  }
1579  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1580  {
1581  $origin_id = $tmp_origin_id;
1582  $ret = $this->add_object_linked($origin, $origin_id);
1583  if (!$ret) {
1584  dol_print_error($this->db);
1585  $error++;
1586  }
1587  }
1588  }
1589  }
1590  }
1591 
1592  if (!$error) {
1593  $result = $this->insertExtraFields();
1594  if ($result < 0) {
1595  $error++;
1596  }
1597  }
1598 
1599  if (!$error && !$notrigger) {
1600  // Call trigger
1601  $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1602  if ($result < 0) {
1603  $this->db->rollback();
1604 
1605  return -1;
1606  }
1607  // End call triggers
1608  }
1609 
1610  $this->db->commit();
1611  return $this->id;
1612  } else {
1613  $this->error = $this->db->lasterror();
1614  $this->db->rollback();
1615 
1616  return -2;
1617  }
1618  } else {
1619  $this->error = 'Failed to get ID of inserted line';
1620 
1621  return -1;
1622  }
1623  } else {
1624  $this->error = $this->db->lasterror();
1625  $this->db->rollback();
1626 
1627  return -1;
1628  }
1629  }
1630 
1638  public function update(User $user, $notrigger = 0)
1639  {
1640  global $conf;
1641 
1642  $error = 0;
1643 
1644  // Clean parameters
1645  if (isset($this->ref)) {
1646  $this->ref = trim($this->ref);
1647  }
1648  if (isset($this->ref_supplier)) {
1649  $this->ref_supplier = trim($this->ref_supplier);
1650  }
1651  if (isset($this->note_private)) {
1652  $this->note_private = trim($this->note_private);
1653  }
1654  if (isset($this->note_public)) {
1655  $this->note_public = trim($this->note_public);
1656  }
1657  if (isset($this->model_pdf)) {
1658  $this->model_pdf = trim($this->model_pdf);
1659  }
1660  if (isset($this->import_key)) {
1661  $this->import_key = trim($this->import_key);
1662  }
1663 
1664  // Update request
1665  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1666 
1667  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1668  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1669  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1670  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1671  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1672  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1673  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1674  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1675  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1676  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1677  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1678  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1679  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1680  $sql .= " fk_user_valid=".(isset($this->user_valid) && $this->user_valid > 0 ? $this->user_valid : "null").",";
1681  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1682  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1683  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1684  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1685  //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1686  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1687  //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1688  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1689  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1690  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1691  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1692 
1693  $sql .= " WHERE rowid=".((int) $this->id);
1694 
1695  $this->db->begin();
1696 
1697  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1698  $resql = $this->db->query($sql);
1699  if (!$resql) {
1700  $error++;
1701  $this->errors[] = "Error ".$this->db->lasterror();
1702  }
1703 
1704  if (!$error) {
1705  $result = $this->insertExtraFields();
1706  if ($result < 0) {
1707  $error++;
1708  }
1709  }
1710 
1711  if (!$error && !$notrigger) {
1712  // Call trigger
1713  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1714  if ($result < 0) {
1715  $error++;
1716  }
1717  // End call triggers
1718  }
1719 
1720  // Commit or rollback
1721  if ($error) {
1722  foreach ($this->errors as $errmsg) {
1723  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1724  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1725  }
1726  $this->db->rollback();
1727  return -1 * $error;
1728  } else {
1729  $this->db->commit();
1730  return 1;
1731  }
1732  }
1733 
1742  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1743  {
1744  global $conf, $user, $hookmanager;
1745 
1746  $error = 0;
1747 
1748  $this->db->begin();
1749 
1750  // get extrafields so they will be clone
1751  foreach ($this->lines as $line) {
1752  $line->fetch_optionals();
1753  }
1754 
1755  // Load source object
1756  $objFrom = clone $this;
1757 
1758  // Change socid if needed
1759  if (!empty($socid) && $socid != $this->socid) {
1760  $objsoc = new Societe($this->db);
1761 
1762  if ($objsoc->fetch($socid) > 0) {
1763  $this->socid = $objsoc->id;
1764  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1765  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1766  $this->fk_project = 0;
1767  $this->fk_delivery_address = 0;
1768  }
1769 
1770  // TODO Change product price if multi-prices
1771  }
1772 
1773  $this->id = 0;
1774  $this->statut = self::STATUS_DRAFT;
1775 
1776  // Clear fields
1777  $this->user_author_id = $user->id;
1778  $this->user_valid = 0;
1779  $this->date_creation = '';
1780  $this->date_validation = '';
1781  $this->ref_supplier = '';
1782  $this->user_approve_id = '';
1783  $this->user_approve_id2 = '';
1784  $this->date_approve = '';
1785  $this->date_approve2 = '';
1786 
1787  // Create clone
1788  $this->context['createfromclone'] = 'createfromclone';
1789  $result = $this->create($user, $notrigger);
1790  if ($result < 0) {
1791  $error++;
1792  }
1793 
1794  if (!$error) {
1795  // Hook of thirdparty module
1796  if (is_object($hookmanager)) {
1797  $parameters = array('objFrom'=>$objFrom);
1798  $action = '';
1799  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1800  if ($reshook < 0) {
1801  $this->setErrorsFromObject($hookmanager);
1802  $error++;
1803  }
1804  }
1805  }
1806 
1807  unset($this->context['createfromclone']);
1808 
1809  // End
1810  if (!$error) {
1811  $this->db->commit();
1812  return $this->id;
1813  } else {
1814  $this->db->rollback();
1815  return -1;
1816  }
1817  }
1818 
1848  public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $fk_prod_fourn_price = 0, $ref_supplier = '', $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $type = 0, $info_bits = 0, $notrigger = false, $date_start = null, $date_end = null, $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0, $rang = -1, $special_code = 0)
1849  {
1850  global $langs, $mysoc, $conf;
1851 
1852  dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $fk_prod_fourn_price, $ref_supplier, $remise_percent, $price_base_type, $pu_ttc, $type, $info_bits, $notrigger, $date_start, $date_end, $fk_unit, $pu_ht_devise, $origin, $origin_id");
1853  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1854 
1855  if ($this->statut == self::STATUS_DRAFT) {
1856  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1857 
1858  // Clean parameters
1859  if (empty($qty)) {
1860  $qty = 0;
1861  }
1862  if (!$info_bits) {
1863  $info_bits = 0;
1864  }
1865  if (empty($txtva)) {
1866  $txtva = 0;
1867  }
1868  if (empty($rang)) {
1869  $rang = 0;
1870  }
1871  if (empty($txlocaltax1)) {
1872  $txlocaltax1 = 0;
1873  }
1874  if (empty($txlocaltax2)) {
1875  $txlocaltax2 = 0;
1876  }
1877  if (empty($remise_percent)) {
1878  $remise_percent = 0;
1879  }
1880 
1881  $remise_percent = price2num($remise_percent);
1882  $qty = price2num($qty);
1883  $pu_ht = price2num($pu_ht);
1884  $pu_ht_devise = price2num($pu_ht_devise);
1885  $pu_ttc = price2num($pu_ttc);
1886  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1887  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1888  }
1889  $txlocaltax1 = price2num($txlocaltax1);
1890  $txlocaltax2 = price2num($txlocaltax2);
1891  if ($price_base_type == 'HT') {
1892  $pu = $pu_ht;
1893  } else {
1894  $pu = $pu_ttc;
1895  }
1896  $desc = trim($desc);
1897 
1898  // Check parameters
1899  if ($qty < 0 && !$fk_product) {
1900  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1901  return -1;
1902  }
1903  if ($type < 0) {
1904  return -1;
1905  }
1906  if ($date_start && $date_end && $date_start > $date_end) {
1907  $langs->load("errors");
1908  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1909  return -1;
1910  }
1911 
1912 
1913  $this->db->begin();
1914 
1915  $product_type = $type;
1916  $label = ''; // deprecated
1917 
1918  if ($fk_product > 0) {
1919  if (!empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY)) { // Not the common case
1920  // Check quantity is enough
1921  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_prod_fourn_price=".$fk_prod_fourn_price." qty=".$qty." ref_supplier=".$ref_supplier);
1922  $prod = new ProductFournisseur($this->db);
1923  if ($prod->fetch($fk_product) > 0) {
1924  $product_type = $prod->type;
1925  $label = $prod->label;
1926 
1927  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1928  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1929  $result = $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (isset($this->fk_soc) ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
1930 
1931  // If supplier order created from sales order, we take best supplier price
1932  // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1933  if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1934  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1935  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1936  // is remise percent not keyed but present for the product we add it
1937  if ($remise_percent == 0 && $prod->remise_percent != 0) {
1938  $remise_percent = $prod->remise_percent;
1939  }
1940  }
1941  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1942  $langs->load("errors");
1943  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1944  $this->db->rollback();
1945  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1946  //$pu = $prod->fourn_pu; // We do not overwrite unit price
1947  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1948  return -1;
1949  }
1950  if ($result == -1) {
1951  $langs->load("errors");
1952  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1953  $this->db->rollback();
1954  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1955  return -1;
1956  }
1957  if ($result < -1) {
1958  $this->error = $prod->error;
1959  $this->db->rollback();
1960  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1961  return -1;
1962  }
1963  } else {
1964  $this->error = $prod->error;
1965  $this->db->rollback();
1966  return -1;
1967  }
1968  }
1969 
1970  // Predefine quantity according to packaging
1971  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
1972  $prod = new Product($this->db);
1973  $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
1974 
1975  if ($qty < $prod->packaging) {
1976  $qty = $prod->packaging;
1977  } else {
1978  if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1979  $coeff = intval($qty / $prod->packaging) + 1;
1980  $qty = $prod->packaging * $coeff;
1981  setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1982  }
1983  }
1984  }
1985  }
1986 
1987  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1988  $pu = 0;
1989  }
1990 
1991  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1992 
1993  // Clean vat code
1994  $reg = array();
1995  $vat_src_code = '';
1996  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1997  $vat_src_code = $reg[1];
1998  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1999  }
2000 
2001  // Calcul du total TTC et de la TVA pour la ligne a partir de
2002  // qty, pu, remise_percent et txtva
2003  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2004  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2005 
2006  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2007 
2008  $total_ht = $tabprice[0];
2009  $total_tva = $tabprice[1];
2010  $total_ttc = $tabprice[2];
2011  $total_localtax1 = $tabprice[9];
2012  $total_localtax2 = $tabprice[10];
2013  $pu = $pu_ht = $tabprice[3];
2014 
2015  // MultiCurrency
2016  $multicurrency_total_ht = $tabprice[16];
2017  $multicurrency_total_tva = $tabprice[17];
2018  $multicurrency_total_ttc = $tabprice[18];
2019  $pu_ht_devise = $tabprice[19];
2020 
2021  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2022  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2023 
2024  if ($rang < 0) {
2025  $rangmax = $this->line_max();
2026  $rang = $rangmax + 1;
2027  }
2028 
2029  // Insert line
2030  $this->line = new CommandeFournisseurLigne($this->db);
2031 
2032  $this->line->context = $this->context;
2033 
2034  $this->line->fk_commande = $this->id;
2035  $this->line->label = $label;
2036  $this->line->ref_fourn = $ref_supplier;
2037  $this->line->ref_supplier = $ref_supplier;
2038  $this->line->desc = $desc;
2039  $this->line->qty = $qty;
2040  $this->line->tva_tx = $txtva;
2041  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2042  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2043  $this->line->localtax1_type = $localtax1_type;
2044  $this->line->localtax2_type = $localtax2_type;
2045  $this->line->fk_product = $fk_product;
2046  $this->line->product_type = $product_type;
2047  $this->line->remise_percent = $remise_percent;
2048  $this->line->subprice = $pu_ht;
2049  $this->line->rang = $rang;
2050  $this->line->info_bits = $info_bits;
2051 
2052  $this->line->vat_src_code = $vat_src_code;
2053  $this->line->total_ht = $total_ht;
2054  $this->line->total_tva = $total_tva;
2055  $this->line->total_localtax1 = $total_localtax1;
2056  $this->line->total_localtax2 = $total_localtax2;
2057  $this->line->total_ttc = $total_ttc;
2058  $this->line->product_type = $type;
2059  $this->line->special_code = (!empty($special_code) ? $special_code : 0);
2060  $this->line->origin = $origin;
2061  $this->line->origin_id = $origin_id;
2062  $this->line->fk_unit = $fk_unit;
2063 
2064  $this->line->date_start = $date_start;
2065  $this->line->date_end = $date_end;
2066 
2067  // Multicurrency
2068  $this->line->fk_multicurrency = $this->fk_multicurrency;
2069  $this->line->multicurrency_code = $this->multicurrency_code;
2070  $this->line->multicurrency_subprice = $pu_ht_devise;
2071  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2072  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2073  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2074 
2075  $this->line->subprice = $pu_ht;
2076  $this->line->price = $this->line->subprice;
2077 
2078  $this->line->remise_percent = $remise_percent;
2079 
2080  if (is_array($array_options) && count($array_options) > 0) {
2081  $this->line->array_options = $array_options;
2082  }
2083 
2084  $result = $this->line->insert($notrigger);
2085  if ($result > 0) {
2086  // Reorder if child line
2087  if (!empty($fk_parent_line)) {
2088  $this->line_order(true, 'DESC');
2089  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2090  $linecount = count($this->lines);
2091  for ($ii = $rang; $ii <= $linecount; $ii++) {
2092  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2093  }
2094  }
2095 
2096  // Mise a jour informations denormalisees au niveau de la commande meme
2097  $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2098  if ($result > 0) {
2099  $this->db->commit();
2100  return $this->line->id;
2101  } else {
2102  $this->db->rollback();
2103  return -1;
2104  }
2105  } else {
2106  $this->error = $this->line->error;
2107  $this->errors = $this->line->errors;
2108  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2109  $this->db->rollback();
2110  return -1;
2111  }
2112  }
2113  return -1;
2114  }
2115 
2116 
2134  public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2135  {
2136  global $conf, $langs;
2137 
2138  $error = 0;
2139  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2140 
2141  // Check parameters (if test are wrong here, there is bug into caller)
2142  if ($entrepot <= 0) {
2143  $this->error = 'ErrorBadValueForParameterWarehouse';
2144  return -1;
2145  }
2146  if ($qty == 0) {
2147  $this->error = 'ErrorBadValueForParameterQty';
2148  return -1;
2149  }
2150 
2151  $dispatchstatus = 1;
2152  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2153  $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2154  }
2155 
2156  $now = dol_now();
2157 
2158  $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2159 
2160  if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2161  $this->db->begin();
2162 
2163  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
2164  $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2165  $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2166  $sql .= ($eatby ? "'".$this->db->idate($eatby)."'" : "null").", ".($sellby ? "'".$this->db->idate($sellby)."'" : "null").", ".($batch ? "'".$this->db->escape($batch)."'" : "null").", ".($fk_reception > 0 ? "'".$this->db->escape($fk_reception)."'" : "null");
2167  $sql .= ")";
2168 
2169  dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2170  $resql = $this->db->query($sql);
2171  if ($resql) {
2172  if (!$notrigger) {
2173  global $conf, $langs, $user;
2174  // Call trigger
2175  $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2176  if ($result < 0) {
2177  $error++;
2178  }
2179  // End call triggers
2180  }
2181  } else {
2182  $this->error = $this->db->lasterror();
2183  $error++;
2184  }
2185 
2186  // If module stock is enabled and the stock increase is done on purchase order dispatching
2187  if (!$error && $entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
2188  $mouv = new MouvementStock($this->db);
2189  if ($product > 0) {
2190  // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2191  $mouv->origin = &$this;
2192  $mouv->setOrigin($this->element, $this->id);
2193 
2194  // Method change if qty < 0
2195  if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qty < 0) {
2196  $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2197  } else {
2198  $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2199  }
2200 
2201  if ($result < 0) {
2202  $this->error = $mouv->error;
2203  $this->errors = $mouv->errors;
2204  dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2205  $error++;
2206  }
2207  }
2208  }
2209 
2210  if ($error == 0) {
2211  $this->db->commit();
2212  return 1;
2213  } else {
2214  $this->db->rollback();
2215  return -1;
2216  }
2217  } else {
2218  $this->error = 'BadStatusForObject';
2219  return -2;
2220  }
2221  }
2222 
2230  public function deleteline($idline, $notrigger = 0)
2231  {
2232  if ($this->statut == 0) {
2233  $line = new CommandeFournisseurLigne($this->db);
2234 
2235  if ($line->fetch($idline) <= 0) {
2236  return 0;
2237  }
2238 
2239  // check if not yet received
2240  $dispatchedLines = $this->getDispachedLines();
2241  foreach ($dispatchedLines as $dispatchLine) {
2242  if ($dispatchLine['orderlineid'] == $idline) {
2243  $this->error = "LineAlreadyDispatched";
2244  $this->errors[] = $this->error;
2245  return -3;
2246  }
2247  }
2248 
2249  if ($line->delete($notrigger) > 0) {
2250  $this->update_price(1);
2251  return 1;
2252  } else {
2253  $this->error = $line->error;
2254  $this->errors = $line->errors;
2255  return -1;
2256  }
2257  } else {
2258  return -2;
2259  }
2260  }
2261 
2269  public function delete(User $user, $notrigger = 0)
2270  {
2271  global $langs, $conf;
2272  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2273 
2274  $error = 0;
2275 
2276  $this->db->begin();
2277 
2278  if (empty($notrigger)) {
2279  // Call trigger
2280  $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2281  if ($result < 0) {
2282  $this->errors[] = 'ErrorWhenRunningTrigger';
2283  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2284  $this->db->rollback();
2285  return -1;
2286  }
2287  // End call triggers
2288  }
2289 
2290  // Test we can delete
2291  $this->fetchObjectLinked(null, 'order_supplier');
2292  if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2293  foreach ($this->linkedObjects['reception'] as $element) {
2294  if ($element->statut >= 0) {
2295  $this->errors[] = $langs->trans('ReceptionExist');
2296  $error++;
2297  break;
2298  }
2299  }
2300  }
2301 
2302  $main = MAIN_DB_PREFIX.'commande_fournisseurdet';
2303  $ef = $main."_extrafields";
2304  $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2305  dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2306  if (!$this->db->query($sql)) {
2307  $this->error = $this->db->lasterror();
2308  $this->errors[] = $this->db->lasterror();
2309  $error++;
2310  }
2311 
2312  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2313  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2314  if (!$this->db->query($sql)) {
2315  $this->error = $this->db->lasterror();
2316  $this->errors[] = $this->db->lasterror();
2317  $error++;
2318  }
2319 
2320  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE rowid =".((int) $this->id);
2321  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2322  if ($resql = $this->db->query($sql)) {
2323  if ($this->db->affected_rows($resql) < 1) {
2324  $this->error = $this->db->lasterror();
2325  $this->errors[] = $this->db->lasterror();
2326  $error++;
2327  }
2328  } else {
2329  $this->error = $this->db->lasterror();
2330  $this->errors[] = $this->db->lasterror();
2331  $error++;
2332  }
2333 
2334  // Remove extrafields
2335  if (!$error) {
2336  $result = $this->deleteExtraFields();
2337  if ($result < 0) {
2338  $this->error = 'FailToDeleteExtraFields';
2339  $this->errors[] = 'FailToDeleteExtraFields';
2340  $error++;
2341  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2342  }
2343  }
2344 
2345  // Delete linked object
2346  $res = $this->deleteObjectLinked();
2347  if ($res < 0) {
2348  $this->error = 'FailToDeleteObjectLinked';
2349  $this->errors[] = 'FailToDeleteObjectLinked';
2350  $error++;
2351  }
2352 
2353  if (!$error) {
2354  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2355  $this->deleteEcmFiles();
2356 
2357  // We remove directory
2358  $ref = dol_sanitizeFileName($this->ref);
2359  if ($conf->fournisseur->commande->dir_output) {
2360  $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2361  $file = $dir."/".$ref.".pdf";
2362  if (file_exists($file)) {
2363  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2364  $this->error = 'ErrorFailToDeleteFile';
2365  $this->errors[] = 'ErrorFailToDeleteFile';
2366  $error++;
2367  }
2368  }
2369  if (file_exists($dir)) {
2370  $res = @dol_delete_dir_recursive($dir);
2371  if (!$res) {
2372  $this->error = 'ErrorFailToDeleteDir';
2373  $this->errors[] = 'ErrorFailToDeleteDir';
2374  $error++;
2375  }
2376  }
2377  }
2378  }
2379 
2380  if (!$error) {
2381  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2382  $this->db->commit();
2383  return 1;
2384  } else {
2385  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2386  $this->db->rollback();
2387  return -$error;
2388  }
2389  }
2390 
2391  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2397  public function get_methodes_commande()
2398  {
2399  // phpcs:enable
2400  $sql = "SELECT rowid, libelle";
2401  $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method";
2402  $sql .= " WHERE active = 1";
2403 
2404  $resql = $this->db->query($sql);
2405  if ($resql) {
2406  $i = 0;
2407  $num = $this->db->num_rows($resql);
2408  $this->methodes_commande = array();
2409  while ($i < $num) {
2410  $row = $this->db->fetch_row($resql);
2411 
2412  $this->methodes_commande[$row[0]] = $row[1];
2413 
2414  $i++;
2415  }
2416  return 0;
2417  } else {
2418  return -1;
2419  }
2420  }
2421 
2430  public function getDispachedLines($status = -1)
2431  {
2432  $ret = array();
2433 
2434  // List of already dispatched lines
2435  $sql = "SELECT p.ref, p.label,";
2436  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2437  $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_commandefourndet";
2438  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
2439  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2440  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2441  $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2442  $sql .= " AND cfd.fk_product = p.rowid";
2443  if ($status >= 0) {
2444  $sql .= " AND cfd.status = ".((int) $status);
2445  }
2446  $sql .= " ORDER BY cfd.rowid ASC";
2447 
2448  $resql = $this->db->query($sql);
2449  if ($resql) {
2450  $num = $this->db->num_rows($resql);
2451  $i = 0;
2452 
2453  while ($i < $num) {
2454  $objp = $this->db->fetch_object($resql);
2455  if ($objp) {
2456  $ret[] = array(
2457  'id' => $objp->dispatchedlineid,
2458  'productid' => $objp->fk_product,
2459  'warehouseid' => $objp->warehouse_id,
2460  'qty' => $objp->qty,
2461  'orderlineid' => $objp->fk_commandefourndet
2462  );
2463  }
2464 
2465  $i++;
2466  }
2467  } else {
2468  dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2469  }
2470 
2471  return $ret;
2472  }
2473 
2474  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2484  public function Livraison($user, $date, $type, $comment)
2485  {
2486  // phpcs:enable
2487  global $conf, $langs;
2488 
2489  $result = 0;
2490  $error = 0;
2491 
2492  dol_syslog(get_class($this)."::Livraison");
2493 
2494  $usercanreceive = 0;
2495  if (!isModEnabled('reception')) {
2496  $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2497  } else {
2498  $usercanreceive = $user->hasRight("reception", "creer");
2499  }
2500 
2501  if ($usercanreceive) {
2502  // Define the new status
2503  if ($type == 'par') {
2505  } elseif ($type == 'tot') {
2507  } elseif ($type == 'nev') {
2509  } elseif ($type == 'can') {
2511  } else {
2512  $error++;
2513  dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2514  return -2;
2515  }
2516 
2517  // Some checks to accept the record
2518  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2519  // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2520  if (!$error && ($type == 'tot')) {
2521  $dispatchedlinearray = $this->getDispachedLines(0);
2522  if (count($dispatchedlinearray) > 0) {
2523  $result = -1;
2524  $error++;
2525  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2526  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2527  }
2528  }
2529  if (!$error && !empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS_NEED_APPROVE) && ($type == 'tot')) { // Accept to move to reception done, only if status of all line are ok (refuse denied)
2530  $dispatcheddenied = $this->getDispachedLines(2);
2531  if (count($dispatchedlinearray) > 0) {
2532  $result = -1;
2533  $error++;
2534  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2535  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2536  }
2537  }
2538  }
2539 
2540  // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2541 
2542  if (empty($error)) {
2543  $this->db->begin();
2544 
2545  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2546  $sql .= " SET fk_statut = ".((int) $statut);
2547  $sql .= " WHERE rowid = ".((int) $this->id);
2548  $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2549 
2550  dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2551  $resql = $this->db->query($sql);
2552  if ($resql) {
2553  $result = 1;
2554  $old_statut = $this->statut;
2555  $this->statut = $statut;
2556  $this->actionmsg2 = $comment;
2557 
2558  // Call trigger
2559  $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2560  if ($result_trigger < 0) {
2561  $error++;
2562  }
2563  // End call triggers
2564 
2565  if (empty($error)) {
2566  $this->db->commit();
2567  } else {
2568  $this->statut = $old_statut;
2569  $this->db->rollback();
2570  $this->error = $this->db->lasterror();
2571  $result = -1;
2572  }
2573  } else {
2574  $this->db->rollback();
2575  $this->error = $this->db->lasterror();
2576  $result = -1;
2577  }
2578  }
2579  } else {
2580  $this->error = $langs->trans('NotAuthorized');
2581  $this->errors[] = $langs->trans('NotAuthorized');
2582  dol_syslog(get_class($this)."::Livraison Not Authorized");
2583  $result = -3;
2584  }
2585  return $result;
2586  }
2587 
2588  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2598  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2599  {
2600  // phpcs:enable
2601  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2602  }
2603 
2612  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2613  {
2614  if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2615  $error = 0;
2616 
2617  $this->db->begin();
2618 
2619  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2620  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2621  $sql .= " WHERE rowid = ".((int) $this->id);
2622 
2623  dol_syslog(__METHOD__, LOG_DEBUG);
2624  $resql = $this->db->query($sql);
2625  if (!$resql) {
2626  $this->errors[] = $this->db->error();
2627  $error++;
2628  }
2629 
2630  if (!$error) {
2631  $this->oldcopy = clone $this;
2632  $this->date_livraison = $delivery_date;
2633  $this->delivery_date = $delivery_date;
2634  }
2635 
2636  if (!$notrigger && empty($error)) {
2637  // Call trigger
2638  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2639  if ($result < 0) {
2640  $error++;
2641  }
2642  // End call triggers
2643  }
2644 
2645  if (!$error) {
2646  $this->db->commit();
2647  return 1;
2648  } else {
2649  foreach ($this->errors as $errmsg) {
2650  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2651  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2652  }
2653  $this->db->rollback();
2654  return -1 * $error;
2655  }
2656  } else {
2657  return -2;
2658  }
2659  }
2660 
2661  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2670  public function set_id_projet($user, $id_projet, $notrigger = 0)
2671  {
2672  // phpcs:enable
2673  if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2674  $error = 0;
2675 
2676  $this->db->begin();
2677 
2678  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2679  $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2680  $sql .= " WHERE rowid = ".((int) $this->id);
2681 
2682  dol_syslog(__METHOD__, LOG_DEBUG);
2683  $resql = $this->db->query($sql);
2684  if (!$resql) {
2685  $this->errors[] = $this->db->error();
2686  $error++;
2687  }
2688 
2689  if (!$error) {
2690  $this->oldcopy = clone $this;
2691  $this->fk_projet = $id_projet;
2692  $this->fk_project = $id_projet;
2693  }
2694 
2695  if (!$notrigger && empty($error)) {
2696  // Call trigger
2697  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2698  if ($result < 0) {
2699  $error++;
2700  }
2701  // End call triggers
2702  }
2703 
2704  if (!$error) {
2705  $this->db->commit();
2706  return 1;
2707  } else {
2708  foreach ($this->errors as $errmsg) {
2709  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2710  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2711  }
2712  $this->db->rollback();
2713  return -1 * $error;
2714  }
2715  } else {
2716  return -2;
2717  }
2718  }
2719 
2728  public function updateFromCommandeClient($user, $idc, $comclientid)
2729  {
2730  $comclient = new Commande($this->db);
2731  $comclient->fetch($comclientid);
2732 
2733  $this->id = $idc;
2734 
2735  $this->lines = array();
2736 
2737  $num = count($comclient->lines);
2738  for ($i = 0; $i < $num; $i++) {
2739  $prod = new Product($this->db);
2740  $label = '';
2741  $ref = '';
2742  if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2743  $label = $prod->label;
2744  $ref = $prod->ref;
2745  }
2746 
2747  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2748  $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2749  $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2750  $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2751  $sql .= ", ".price2num($comclient->lines[$i]->qty, 'MS').", ".price2num($comclient->lines[$i]->tva_tx, 5).", ".price2num($comclient->lines[$i]->localtax1_tx, 5).", ".price2num($comclient->lines[$i]->localtax2_tx, 5).", ".price2num($comclient->lines[$i]->remise_percent, 3);
2752  $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2753  if ($this->db->query($sql)) {
2754  $this->update_price(1);
2755  }
2756  }
2757 
2758  return 1;
2759  }
2760 
2768  public function setStatus($user, $status)
2769  {
2770  global $conf, $langs;
2771  $error = 0;
2772 
2773  $this->db->begin();
2774 
2775  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2776  $sql .= " SET fk_statut = ".$status;
2777  $sql .= " WHERE rowid = ".((int) $this->id);
2778 
2779  dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2780  $resql = $this->db->query($sql);
2781  if ($resql) {
2782  // Trigger names for each status
2783  $triggerName = array();
2784  $triggerName[0] = 'DRAFT';
2785  $triggerName[1] = 'VALIDATED';
2786  $triggerName[2] = 'APPROVED';
2787  $triggerName[3] = 'ORDERED'; // Ordered
2788  $triggerName[4] = 'RECEIVED_PARTIALLY';
2789  $triggerName[5] = 'RECEIVED_COMPLETELY';
2790  $triggerName[6] = 'CANCELED';
2791  $triggerName[7] = 'CANCELED';
2792  $triggerName[9] = 'REFUSED';
2793 
2794  // Call trigger
2795  $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2796  if ($result < 0) {
2797  $error++;
2798  }
2799  // End call triggers
2800  } else {
2801  $error++;
2802  $this->error = $this->db->lasterror();
2803  dol_syslog(get_class($this)."::setStatus ".$this->error);
2804  }
2805 
2806  if (!$error) {
2807  $this->statut = $status;
2808  $this->db->commit();
2809  return 1;
2810  } else {
2811  $this->db->rollback();
2812  return -1;
2813  }
2814  }
2815 
2839  public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $notrigger = 0, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
2840  {
2841  global $mysoc, $conf, $langs;
2842  dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2843  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2844 
2845  $error = 0;
2846 
2847  if ($this->brouillon) {
2848  // Clean parameters
2849  if (empty($qty)) {
2850  $qty = 0;
2851  }
2852  if (empty($info_bits)) {
2853  $info_bits = 0;
2854  }
2855  if (empty($txtva)) {
2856  $txtva = 0;
2857  }
2858  if (empty($txlocaltax1)) {
2859  $txlocaltax1 = 0;
2860  }
2861  if (empty($txlocaltax2)) {
2862  $txlocaltax2 = 0;
2863  }
2864  if (empty($remise)) {
2865  $remise = 0;
2866  }
2867  if (empty($remise_percent)) {
2868  $remise_percent = 0;
2869  }
2870 
2871  $remise_percent = price2num($remise_percent);
2872  $qty = price2num($qty);
2873  if (!$qty) {
2874  $qty = 1;
2875  }
2876  $pu = price2num($pu);
2877  $pu_ht_devise = price2num($pu_ht_devise);
2878  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2879  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2880  }
2881  $txlocaltax1 = price2num($txlocaltax1);
2882  $txlocaltax2 = price2num($txlocaltax2);
2883 
2884  // Check parameters
2885  if ($type < 0) {
2886  return -1;
2887  }
2888  if ($date_start && $date_end && $date_start > $date_end) {
2889  $langs->load("errors");
2890  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2891  return -1;
2892  }
2893 
2894  $this->db->begin();
2895 
2896  // Calcul du total TTC et de la TVA pour la ligne a partir de
2897  // qty, pu, remise_percent et txtva
2898  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2899  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2900 
2901  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2902 
2903  // Clean vat code
2904  $reg = array();
2905  $vat_src_code = '';
2906  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2907  $vat_src_code = $reg[1];
2908  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2909  }
2910 
2911  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2912  $total_ht = $tabprice[0];
2913  $total_tva = $tabprice[1];
2914  $total_ttc = $tabprice[2];
2915  $total_localtax1 = $tabprice[9];
2916  $total_localtax2 = $tabprice[10];
2917  $pu_ht = $tabprice[3];
2918  $pu_tva = $tabprice[4];
2919  $pu_ttc = $tabprice[5];
2920 
2921  // MultiCurrency
2922  $multicurrency_total_ht = $tabprice[16];
2923  $multicurrency_total_tva = $tabprice[17];
2924  $multicurrency_total_ttc = $tabprice[18];
2925  $pu_ht_devise = $tabprice[19];
2926 
2927  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2928  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2929 
2930  //Fetch current line from the database and then clone the object and set it in $oldline property
2931  $this->line = new CommandeFournisseurLigne($this->db);
2932  $this->line->fetch($rowid);
2933 
2934  $oldline = clone $this->line;
2935  $this->line->oldline = $oldline;
2936 
2937  $this->line->context = $this->context;
2938 
2939  $this->line->fk_commande = $this->id;
2940  //$this->line->label=$label;
2941  $this->line->desc = $desc;
2942 
2943  // redefine quantity according to packaging
2944  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
2945  if ($qty < $this->line->packaging) {
2946  $qty = $this->line->packaging;
2947  } else {
2948  if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2949  $coeff = intval($qty / $this->line->packaging) + 1;
2950  $qty = $this->line->packaging * $coeff;
2951  setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2952  }
2953  }
2954  }
2955 
2956  $this->line->qty = $qty;
2957  $this->line->ref_supplier = $ref_supplier;
2958 
2959  $this->line->vat_src_code = $vat_src_code;
2960  $this->line->tva_tx = $txtva;
2961  $this->line->localtax1_tx = $txlocaltax1;
2962  $this->line->localtax2_tx = $txlocaltax2;
2963  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2964  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2965  $this->line->remise_percent = $remise_percent;
2966  $this->line->subprice = $pu_ht;
2967  $this->line->info_bits = $info_bits;
2968  $this->line->total_ht = $total_ht;
2969  $this->line->total_tva = $total_tva;
2970  $this->line->total_localtax1 = $total_localtax1;
2971  $this->line->total_localtax2 = $total_localtax2;
2972  $this->line->total_ttc = $total_ttc;
2973  $this->line->product_type = $type;
2974  $this->line->special_code = $oldline->special_code;
2975  $this->line->rang = $oldline->rang;
2976  $this->line->origin = $this->origin;
2977  $this->line->fk_unit = $fk_unit;
2978 
2979  $this->line->date_start = $date_start;
2980  $this->line->date_end = $date_end;
2981 
2982  // Multicurrency
2983  $this->line->fk_multicurrency = $this->fk_multicurrency;
2984  $this->line->multicurrency_code = $this->multicurrency_code;
2985  $this->line->multicurrency_subprice = $pu_ht_devise;
2986  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2987  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2988  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2989 
2990  $this->line->subprice = $pu_ht;
2991  $this->line->price = $this->line->subprice;
2992 
2993  $this->line->remise_percent = $remise_percent;
2994 
2995  if (is_array($array_options) && count($array_options) > 0) {
2996  // We replace values in this->line->array_options only for entries defined into $array_options
2997  foreach ($array_options as $key => $value) {
2998  $this->line->array_options[$key] = $array_options[$key];
2999  }
3000  }
3001 
3002  $result = $this->line->update($notrigger);
3003 
3004 
3005  // Mise a jour info denormalisees au niveau facture
3006  if ($result >= 0) {
3007  $this->update_price('1', 'auto');
3008  $this->db->commit();
3009  return $result;
3010  } else {
3011  $this->error = $this->db->lasterror();
3012  $this->db->rollback();
3013  return -1;
3014  }
3015  } else {
3016  $this->error = "Order status makes operation forbidden";
3017  dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3018  return -2;
3019  }
3020  }
3021 
3022 
3030  public function initAsSpecimen()
3031  {
3032  global $user, $langs, $conf;
3033 
3034  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3035 
3036  dol_syslog(get_class($this)."::initAsSpecimen");
3037 
3038  $now = dol_now();
3039 
3040  // Find first product
3041  $prodid = 0;
3042  $product = new ProductFournisseur($this->db);
3043  $sql = "SELECT rowid";
3044  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3045  $sql .= " WHERE entity IN (".getEntity('product').")";
3046  $sql .= $this->db->order("rowid", "ASC");
3047  $sql .= $this->db->plimit(1);
3048  $resql = $this->db->query($sql);
3049  if ($resql) {
3050  $obj = $this->db->fetch_object($resql);
3051  $prodid = $obj->rowid;
3052  }
3053 
3054  // Initialise parametres
3055  $this->id = 0;
3056  $this->ref = 'SPECIMEN';
3057  $this->specimen = 1;
3058  $this->socid = 1;
3059  $this->date = $now;
3060  $this->date_commande = $now;
3061  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3062  $this->cond_reglement_code = 'RECEP';
3063  $this->mode_reglement_code = 'CHQ';
3064 
3065  $this->note_public = 'This is a comment (public)';
3066  $this->note_private = 'This is a comment (private)';
3067 
3068  $this->multicurrency_tx = 1;
3069  $this->multicurrency_code = $conf->currency;
3070 
3071  $this->statut = 0;
3072 
3073  // Lines
3074  $nbp = 5;
3075  $xnbp = 0;
3076  while ($xnbp < $nbp) {
3077  $line = new CommandeFournisseurLigne($this->db);
3078  $line->desc = $langs->trans("Description")." ".$xnbp;
3079  $line->qty = 1;
3080  $line->subprice = 100;
3081  $line->price = 100;
3082  $line->tva_tx = 19.6;
3083  $line->localtax1_tx = 0;
3084  $line->localtax2_tx = 0;
3085  if ($xnbp == 2) {
3086  $line->total_ht = 50;
3087  $line->total_ttc = 59.8;
3088  $line->total_tva = 9.8;
3089  $line->remise_percent = 50;
3090  } else {
3091  $line->total_ht = 100;
3092  $line->total_ttc = 119.6;
3093  $line->total_tva = 19.6;
3094  $line->remise_percent = 00;
3095  }
3096  $line->fk_product = $prodid;
3097 
3098  $this->lines[$xnbp] = $line;
3099 
3100  $this->total_ht += $line->total_ht;
3101  $this->total_tva += $line->total_tva;
3102  $this->total_ttc += $line->total_ttc;
3103 
3104  $xnbp++;
3105  }
3106  }
3107 
3114  public function info($id)
3115  {
3116  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3117  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3118  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
3119  $sql .= ' WHERE c.rowid = '.((int) $id);
3120 
3121  $result = $this->db->query($sql);
3122  if ($result) {
3123  if ($this->db->num_rows($result)) {
3124  $obj = $this->db->fetch_object($result);
3125  $this->id = $obj->rowid;
3126  if ($obj->fk_user_author) {
3127  $this->user_creation_id = $obj->fk_user_author;
3128  }
3129  if ($obj->fk_user_valid) {
3130  $this->user_validation_id = $obj->fk_user_valid;
3131  }
3132  if ($obj->fk_user_modif) {
3133  $this->user_modification_id = $obj->fk_user_modif;
3134  }
3135  if ($obj->fk_user_approve) {
3136  $this->user_approve_id = $obj->fk_user_approve;
3137  }
3138  if ($obj->fk_user_approve2) {
3139  $this->user_approve_id2 = $obj->fk_user_approve2;
3140  }
3141 
3142  $this->date_creation = $this->db->jdate($obj->datec);
3143  $this->date_modification = $this->db->jdate($obj->datem);
3144  $this->date_approve = $this->db->jdate($obj->datea);
3145  $this->date_approve2 = $this->db->jdate($obj->datea2);
3146  $this->date_validation = $this->db->jdate($obj->date_validation);
3147  }
3148  $this->db->free($result);
3149  } else {
3150  dol_print_error($this->db);
3151  }
3152  }
3153 
3154  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3160  public function load_state_board()
3161  {
3162  // phpcs:enable
3163  global $conf, $user;
3164 
3165  $this->nb = array();
3166  $clause = "WHERE";
3167 
3168  $sql = "SELECT count(co.rowid) as nb";
3169  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
3170  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3171  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3172  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3173  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3174  $clause = "AND";
3175  }
3176  $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3177 
3178  $resql = $this->db->query($sql);
3179  if ($resql) {
3180  while ($obj = $this->db->fetch_object($resql)) {
3181  $this->nb["supplier_orders"] = $obj->nb;
3182  }
3183  $this->db->free($resql);
3184  return 1;
3185  } else {
3186  dol_print_error($this->db);
3187  $this->error = $this->db->error();
3188  return -1;
3189  }
3190  }
3191 
3192  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3200  public function load_board($user, $mode = 'opened')
3201  {
3202  // phpcs:enable
3203  global $conf, $langs;
3204 
3205  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3206  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
3207  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3208  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3209  }
3210  $sql .= " WHERE c.entity = ".$conf->entity;
3211  if ($mode === 'awaiting') {
3212  $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3213  } else {
3214  $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3215  }
3216  if ($user->socid) {
3217  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3218  }
3219 
3220  $resql = $this->db->query($sql);
3221  if ($resql) {
3222  $commandestatic = new CommandeFournisseur($this->db);
3223 
3224  $response = new WorkboardResponse();
3225  $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3226  $response->label = $langs->trans("SuppliersOrdersToProcess");
3227  $response->labelShort = $langs->trans("Opened");
3228  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3229  $response->img = img_object('', "order");
3230 
3231  if ($mode === 'awaiting') {
3232  $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3233  $response->labelShort = $langs->trans("AwaitingReception");
3234  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3235  }
3236 
3237  while ($obj = $this->db->fetch_object($resql)) {
3238  $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3239  $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3240  $commandestatic->statut = $obj->fk_statut;
3241 
3242  $response->nbtodo++;
3243  $response->total += $obj->total_ht;
3244 
3245  if ($commandestatic->hasDelay()) {
3246  $response->nbtodolate++;
3247  }
3248  }
3249 
3250  return $response;
3251  } else {
3252  $this->error = $this->db->error();
3253  return -1;
3254  }
3255  }
3256 
3263  public function getInputMethod()
3264  {
3265  global $db, $langs;
3266 
3267  if ($this->methode_commande_id > 0) {
3268  $sql = "SELECT rowid, code, libelle as label";
3269  $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_method';
3270  $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3271 
3272  $resql = $this->db->query($sql);
3273  if ($resql) {
3274  if ($this->db->num_rows($resql)) {
3275  $obj = $this->db->fetch_object($resql);
3276 
3277  $string = $langs->trans($obj->code);
3278  if ($string == $obj->code) {
3279  $string = $obj->label != '-' ? $obj->label : '';
3280  }
3281  return $string;
3282  }
3283  } else {
3284  dol_print_error($this->db);
3285  }
3286  }
3287 
3288  return '';
3289  }
3290 
3302  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3303  {
3304  global $conf, $langs;
3305 
3306  if (!dol_strlen($modele)) {
3307  $modele = ''; // No doc template/generation by default
3308 
3309  if (!empty($this->model_pdf)) {
3310  $modele = $this->model_pdf;
3311  } elseif (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
3312  $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3313  }
3314  }
3315 
3316  if (empty($modele)) {
3317  return 0;
3318  } else {
3319  $langs->load("suppliers");
3320  $outputlangs->load("products");
3321 
3322  $modelpath = "core/modules/supplier_order/doc/";
3323  $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3324  return $result;
3325  }
3326  }
3327 
3334  public function getMaxDeliveryTimeDay($langs)
3335  {
3336  if (empty($this->lines)) {
3337  return '';
3338  }
3339 
3340  $obj = new ProductFournisseur($this->db);
3341 
3342  $nb = 0;
3343  foreach ($this->lines as $line) {
3344  if ($line->fk_product > 0) {
3345  $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3346  if ($idp) {
3347  $obj->fetch($idp);
3348  if ($obj->delivery_time_days > $nb) {
3349  $nb = $obj->delivery_time_days;
3350  }
3351  }
3352  }
3353  }
3354 
3355  if ($nb === 0) {
3356  return '';
3357  } else {
3358  return $nb.' '.$langs->trans('Days');
3359  }
3360  }
3361 
3366  public function getRights()
3367  {
3368  global $user;
3369 
3370  return $user->hasRight("fournisseur", "commande");
3371  }
3372 
3373 
3382  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3383  {
3384  $tables = array(
3385  'commande_fournisseur'
3386  );
3387 
3388  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3389  }
3390 
3399  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3400  {
3401  $tables = array(
3402  'commande_fournisseurdet'
3403  );
3404 
3405  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3406  }
3407 
3415  public function hasDelay()
3416  {
3417  global $conf;
3418 
3419  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3420  $this->delivery_date = $this->date_livraison; // For backward compatibility
3421  }
3422 
3423  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3424  $now = dol_now();
3425  if (!empty($this->delivery_date)) {
3426  $date_to_test = $this->delivery_date;
3427  return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3428  } else {
3429  //$date_to_test = $this->date_commande;
3430  //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3431  return false;
3432  }
3433  } else {
3434  $now = dol_now();
3435  $date_to_test = $this->date_commande;
3436 
3437  return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3438  }
3439  }
3440 
3448  public function showDelay()
3449  {
3450  global $conf, $langs;
3451 
3452  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3453  $this->delivery_date = $this->date_livraison; // For backward compatibility
3454  }
3455 
3456  $text = '';
3457 
3458  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3459  if (!empty($this->delivery_date)) {
3460  $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3461  } else {
3462  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3463  }
3464  } else {
3465  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3466  }
3467  if ($text) {
3468  $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3469  }
3470 
3471  return $text;
3472  }
3473 
3474 
3483  public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3484  {
3485  global $conf, $langs;
3486 
3487  if (isModEnabled("supplier_order")) {
3488  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3489 
3490  $qtydelivered = array();
3491  $qtywished = array();
3492 
3493  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3494  $filter = array('t.fk_commande'=>$this->id);
3495  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3496  $filter['t.status'] = 1; // Restrict to lines with status validated
3497  }
3498 
3499  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3500  if ($ret < 0) {
3501  $this->error = $supplierorderdispatch->error; $this->errors = $supplierorderdispatch->errors;
3502  return $ret;
3503  } else {
3504  if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3505  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3506  $date_liv = dol_now();
3507 
3508  // Build array with quantity deliverd by product
3509  foreach ($supplierorderdispatch->lines as $line) {
3510  $qtydelivered[$line->fk_product] += $line->qty;
3511  }
3512  foreach ($this->lines as $line) {
3513  // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3514  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $line->product_type > 0) {
3515  continue;
3516  }
3517  $qtywished[$line->fk_product] += $line->qty;
3518  }
3519 
3520  //Compare array
3521  $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3522  $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3523  $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3524  //var_dump(array_keys($qtydelivered));
3525  //var_dump(array_keys($qtywished));
3526  //var_dump($diff_array);
3527  //var_dump($keysinwishednotindelivered);
3528  //var_dump($keysindeliverednotinwished);
3529  //exit;
3530 
3531  if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3532  if ($closeopenorder) {
3533  //$ret=$this->setStatus($user,5);
3534  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3535  if ($ret < 0) {
3536  return -1;
3537  }
3538  return 5;
3539  } else {
3540  //Diff => received partially
3541  //$ret=$this->setStatus($user,4);
3542  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3543  if ($ret < 0) {
3544  return -1;
3545  }
3546  return 4;
3547  }
3548  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
3549  //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3550 
3551  $close = 0;
3552 
3553  if (count($diff_array) > 0) {
3554  //there are some difference between the two arrays
3555 
3556  //scan the array of results
3557  foreach ($diff_array as $key => $value) {
3558  //if the quantity delivered is greater or equal to wish quantity
3559  if ($qtydelivered[$key] >= $qtywished[$key]) {
3560  $close++;
3561  }
3562  }
3563  }
3564 
3565 
3566  if ($close == count($diff_array)) {
3567  //all the products are received equal or more than the wished quantity
3568  if ($closeopenorder) {
3569  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3570  if ($ret < 0) {
3571  return -1;
3572  }
3573  return 5;
3574  } else {
3575  //Diff => received partially
3576  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3577  if ($ret < 0) {
3578  return -1;
3579  }
3580  return 4;
3581  }
3582  } else {
3583  //all the products are not received
3584  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3585  if ($ret < 0) {
3586  return -1;
3587  }
3588  return 4;
3589  }
3590  } else {
3591  //Diff => received partially
3592  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3593  if ($ret < 0) {
3594  return -1;
3595  }
3596  return 4;
3597  }
3598  }
3599  return 1;
3600  }
3601  }
3602  return 0;
3603  }
3604 
3612  public function loadReceptions($filtre_statut = -1)
3613  {
3614  $this->receptions = array();
3615 
3616  dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3617 
3618  $sql = 'SELECT cd.rowid, cd.fk_product,';
3619  $sql .= ' sum(cfd.qty) as qty';
3620  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3621  if ($filtre_statut >= 0) {
3622  $sql .= ' '.MAIN_DB_PREFIX.'reception as e,';
3623  }
3624  $sql .= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3625  $sql .= ' WHERE';
3626  if ($filtre_statut >= 0) {
3627  $sql .= ' cfd.fk_reception = e.rowid AND';
3628  }
3629  $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3630  $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3631  if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3632  $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3633  }
3634  if ($filtre_statut >= 0) {
3635  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3636  }
3637  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3638 
3639  $resql = $this->db->query($sql);
3640  if ($resql) {
3641  $num = $this->db->num_rows($resql);
3642  $i = 0;
3643  while ($i < $num) {
3644  $obj = $this->db->fetch_object($resql);
3645  empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3646  $i++;
3647  }
3648  $this->db->free($resql);
3649 
3650  return $num;
3651  } else {
3652  $this->error = $this->db->lasterror();
3653  return -1;
3654  }
3655  }
3656 
3664  public function getKanbanView($option = '', $arraydata = null)
3665  {
3666  global $langs;
3667 
3668  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3669 
3670  $return = '<div class="box-flex-item box-flex-grow-zero">';
3671  $return .= '<div class="info-box info-box-sm">';
3672  $return .= '<span class="info-box-icon bg-infobox-action">';
3673  $return .= img_picto('', $this->picto);
3674  $return .= '</span>';
3675  $return .= '<div class="info-box-content">';
3676  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3677  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3678  if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3679  $return .='<br><span class="info-box-label amount">'.$this->socid.'</span>';
3680  }
3681  if (property_exists($this, 'billed')) {
3682  $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3683  }
3684  if (method_exists($this, 'getLibStatut')) {
3685  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3686  }
3687  $return .= '</div>';
3688  $return .= '</div>';
3689  $return .= '</div>';
3690  return $return;
3691  }
3692 }
3693 
3694 
3695 
3700 {
3704  public $element = 'commande_fournisseurdet';
3705 
3709  public $table_element = 'commande_fournisseurdet';
3710 
3711  public $oldline;
3712 
3717  public $fk_commande;
3718 
3719  // From llx_commande_fournisseurdet
3723  public $fk_parent_line;
3724 
3728  public $fk_facture;
3729 
3730  public $rang = 0;
3731  public $special_code = 0;
3732 
3737  public $pu_ht;
3738 
3739  public $date_start;
3740  public $date_end;
3741 
3742  // From llx_product_fournisseur_price
3743 
3748  public $ref_supplier;
3749 
3755  public $ref_fourn;
3756 
3757  public $remise;
3758 
3759 
3765  public function __construct($db)
3766  {
3767  $this->db = $db;
3768  }
3769 
3776  public function fetch($rowid)
3777  {
3778  global $conf;
3779 
3780  $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3781  $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3782  $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3783  $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3784  $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3785  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3786  $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3787  $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3788  $sql .= ' c.fk_soc as socid';
3789  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3790  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3791  $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3792 
3793  $result = $this->db->query($sql);
3794  if ($result) {
3795  $objp = $this->db->fetch_object($result);
3796 
3797  if (!empty($objp)) {
3798  $this->rowid = $objp->rowid;
3799  $this->id = $objp->rowid;
3800  $this->fk_commande = $objp->fk_commande;
3801  $this->desc = $objp->description;
3802  $this->qty = $objp->qty;
3803  $this->ref_fourn = $objp->ref_supplier;
3804  $this->ref_supplier = $objp->ref_supplier;
3805  $this->subprice = $objp->subprice;
3806  $this->tva_tx = $objp->tva_tx;
3807  $this->localtax1_tx = $objp->localtax1_tx;
3808  $this->localtax2_tx = $objp->localtax2_tx;
3809  $this->localtax1_type = $objp->localtax1_type;
3810  $this->localtax2_type = $objp->localtax2_type;
3811  $this->remise = $objp->remise;
3812  $this->remise_percent = $objp->remise_percent;
3813  $this->fk_product = $objp->fk_product;
3814  $this->info_bits = $objp->info_bits;
3815  $this->total_ht = $objp->total_ht;
3816  $this->total_tva = $objp->total_tva;
3817  $this->total_localtax1 = $objp->total_localtax1;
3818  $this->total_localtax2 = $objp->total_localtax2;
3819  $this->total_ttc = $objp->total_ttc;
3820  $this->product_type = $objp->product_type;
3821  $this->special_code = $objp->special_code;
3822 
3823  $this->ref = $objp->product_ref;
3824 
3825  $this->product_ref = $objp->product_ref;
3826  $this->product_label = $objp->product_label;
3827  $this->product_desc = $objp->product_desc;
3828 
3829  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
3830  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3831  // Move this into another method and call it when required.
3832 
3833  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3834  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
3835  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3836  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3837  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3838  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3839  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3840  $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3841  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3842  $sqlsearchpackage .= " LIMIT 1";
3843 
3844  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3845  if ($resqlsearchpackage) {
3846  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3847  if ($objsearchpackage) {
3848  $this->fk_fournprice = $objsearchpackage->rowid;
3849  $this->packaging = $objsearchpackage->packaging;
3850  }
3851  } else {
3852  $this->error = $this->db->lasterror();
3853  return -1;
3854  }
3855  }
3856 
3857  $this->date_start = $this->db->jdate($objp->date_start);
3858  $this->date_end = $this->db->jdate($objp->date_end);
3859  $this->fk_unit = $objp->fk_unit;
3860 
3861  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3862  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3863  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3864  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3865 
3866  $this->fetch_optionals();
3867 
3868  $this->db->free($result);
3869  return 1;
3870  } else {
3871  $this->error = 'Supplier order line with id='.$rowid.' not found';
3872  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3873  return 0;
3874  }
3875  } else {
3876  dol_print_error($this->db);
3877  return -1;
3878  }
3879  }
3880 
3887  public function insert($notrigger = 0)
3888  {
3889  global $conf, $user;
3890 
3891  $error = 0;
3892 
3893  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3894 
3895  // Clean parameters
3896  if (empty($this->tva_tx)) {
3897  $this->tva_tx = 0;
3898  }
3899  if (empty($this->localtax1_tx)) {
3900  $this->localtax1_tx = 0;
3901  }
3902  if (empty($this->localtax2_tx)) {
3903  $this->localtax2_tx = 0;
3904  }
3905  if (empty($this->localtax1_type)) {
3906  $this->localtax1_type = '0';
3907  }
3908  if (empty($this->localtax2_type)) {
3909  $this->localtax2_type = '0';
3910  }
3911  if (empty($this->total_localtax1)) {
3912  $this->total_localtax1 = 0;
3913  }
3914  if (empty($this->total_localtax2)) {
3915  $this->total_localtax2 = 0;
3916  }
3917  if (empty($this->rang)) {
3918  $this->rang = 0;
3919  }
3920  if (empty($this->remise_percent)) {
3921  $this->remise_percent = 0;
3922  }
3923  if (empty($this->info_bits)) {
3924  $this->info_bits = 0;
3925  }
3926  if (empty($this->special_code)) {
3927  $this->special_code = 0;
3928  }
3929  if (empty($this->fk_parent_line)) {
3930  $this->fk_parent_line = 0;
3931  }
3932  if (empty($this->pa_ht)) {
3933  $this->pa_ht = 0;
3934  }
3935 
3936  // Multicurrency
3937  if (!empty($this->multicurrency_code)) {
3938  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3939  }
3940  if (empty($this->fk_multicurrency)) {
3941  $this->multicurrency_code = $conf->currency;
3942  $this->fk_multicurrency = 0;
3943  $this->multicurrency_tx = 1;
3944  }
3945 
3946  // Check parameters
3947  if ($this->product_type < 0) {
3948  return -1;
3949  }
3950 
3951  $this->db->begin();
3952 
3953  // Insertion dans base de la ligne
3954  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3955  $sql .= " (fk_commande, label, description, date_start, date_end,";
3956  $sql .= " fk_product, product_type, special_code, rang,";
3957  $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3958  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3959  $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3960  $sql .= ")";
3961  $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3962  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3963  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3964  if ($this->fk_product) {
3965  $sql .= $this->fk_product.",";
3966  } else {
3967  $sql .= "null,";
3968  }
3969  $sql .= "'".$this->db->escape($this->product_type)."',";
3970  $sql .= "'".$this->db->escape($this->special_code)."',";
3971  $sql .= "'".$this->db->escape($this->rang)."',";
3972  $sql .= "'".$this->db->escape($this->qty)."', ";
3973  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3974  $sql .= " ".price2num($this->tva_tx).", ";
3975  $sql .= " ".price2num($this->localtax1_tx).",";
3976  $sql .= " ".price2num($this->localtax2_tx).",";
3977  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3978  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3979  $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3980  $sql .= " ".price2num($this->total_ht).",";
3981  $sql .= " ".price2num($this->total_tva).",";
3982  $sql .= " ".price2num($this->total_localtax1).",";
3983  $sql .= " ".price2num($this->total_localtax2).",";
3984  $sql .= " ".price2num($this->total_ttc).",";
3985  $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3986  $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3987  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3988  $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3989  $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3990  $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3991  $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3992  $sql .= ")";
3993 
3994  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3995  $resql = $this->db->query($sql);
3996  if ($resql) {
3997  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3998  $this->rowid = $this->id;
3999 
4000  if (!$error) {
4001  $result = $this->insertExtraFields();
4002  if ($result < 0) {
4003  $error++;
4004  }
4005  }
4006 
4007  if (!$error && !$notrigger) {
4008  // Call trigger
4009  $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
4010  if ($result < 0) {
4011  $error++;
4012  }
4013  // End call triggers
4014  }
4015 
4016  if (!$error) {
4017  $this->db->commit();
4018  return 1;
4019  }
4020 
4021  foreach ($this->errors as $errmsg) {
4022  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4023  $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4024  }
4025  $this->db->rollback();
4026  return -1 * $error;
4027  } else {
4028  $this->errors[] = $this->db->error();
4029  $this->db->rollback();
4030  return -2;
4031  }
4032  }
4039  public function update($notrigger = 0)
4040  {
4041  global $conf, $user;
4042 
4043  $error = 0;
4044 
4045  $this->db->begin();
4046 
4047  // Mise a jour ligne en base
4048  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
4049  $sql .= " description='".$this->db->escape($this->desc)."'";
4050  $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4051  $sql .= ", subprice='".price2num($this->subprice)."'";
4052  //$sql.= ",remise='".price2num($remise)."'";
4053  $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4054 
4055  $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4056  $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4057  $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4058  $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4059  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4060  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4061  $sql .= ", qty='".price2num($this->qty)."'";
4062  $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4063  $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4064  $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4065  $sql .= ", total_ht='".price2num($this->total_ht)."'";
4066  $sql .= ", total_tva='".price2num($this->total_tva)."'";
4067  $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4068  $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4069  $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4070  $sql .= ", product_type=".$this->product_type;
4071  $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4072  $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4073 
4074  // Multicurrency
4075  $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4076  $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4077  $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4078  $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4079 
4080  $sql .= " WHERE rowid = ".((int) $this->id);
4081 
4082  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4083  $resql = $this->db->query($sql);
4084  if ($resql) {
4085  if (!$error) {
4086  $result = $this->insertExtraFields();
4087  if ($result < 0) {
4088  $error++;
4089  }
4090  }
4091 
4092  if (!$error && !$notrigger) {
4093  // Call trigger
4094  $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4095  if ($result < 0) {
4096  $this->db->rollback();
4097  return -1;
4098  }
4099  // End call triggers
4100  }
4101 
4102  if (!$error) {
4103  $this->db->commit();
4104  return 1;
4105  } else {
4106  $this->db->rollback();
4107  return -1;
4108  }
4109  } else {
4110  $this->error = $this->db->lasterror();
4111  $this->db->rollback();
4112  return -1;
4113  }
4114  }
4115 
4122  public function delete($notrigger = 0)
4123  {
4124  global $user;
4125 
4126  $error = 0;
4127 
4128  $this->db->begin();
4129 
4130  // extrafields
4131  $result = $this->deleteExtraFields();
4132  if ($result < 0) {
4133  $this->db->rollback();
4134  return -1;
4135  }
4136 
4137  $sql1 = 'UPDATE '.MAIN_DB_PREFIX."commandedet SET fk_commandefourndet = NULL WHERE rowid=".((int) $this->id);
4138  $resql = $this->db->query($sql1);
4139  if (!$resql) {
4140  $this->db->rollback();
4141  return -1;
4142  }
4143 
4144  $sql2 = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4145 
4146  dol_syslog(__METHOD__, LOG_DEBUG);
4147  $resql = $this->db->query($sql2);
4148  if ($resql) {
4149  if (!$notrigger) {
4150  // Call trigger
4151  $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4152  if ($result < 0) {
4153  $error++;
4154  }
4155  // End call triggers
4156  }
4157 
4158  if (!$error) {
4159  $this->db->commit();
4160  return 1;
4161  }
4162 
4163  foreach ($this->errors as $errmsg) {
4164  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4165  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4166  }
4167  $this->db->rollback();
4168  return -1 * $error;
4169  } else {
4170  $this->error = $this->db->lasterror();
4171  return -1;
4172  }
4173  }
4174 }
$object ref
Definition: info.php:78
Class to manage table commandefournisseurdispatch.
Class to manage predefined suppliers products.
const STATUS_CANCELED_AFTER_ORDER
Order canceled/never received.
const STATUS_RECEIVED_PARTIALLY
Received partially.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set the planned delivery date.
updateFromCommandeClient($user, $idc, $comclientid)
Update a supplier order from a sales order.
deleteline($idline, $notrigger=0)
Delete line.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
getNomUrl($withpicto=0, $option='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clicable name (with picto eventually)
loadReceptions($filtre_statut=-1)
Load array this->receptions of lines of shipments with nb of products sent for each order line Note: ...
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
refuse($user)
Refuse an order.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
info($id)
Charge les informations d'ordre info dans l'objet facture.
const STATUS_CANCELED
Order canceled.
getNextNumRef($soc)
Returns the following order reference not used depending on the numbering model activated defined wit...
fetch_lines($only_product=0)
Load array lines.
getInputMethod()
Returns the translated input method of object (defined if $this->methode_commande_id > 0).
const STATUS_VALIDATED
Validated status.
const STATUS_RECEIVED_COMPLETELY
Received completely.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $fk_prod_fourn_price=0, $ref_supplier='', $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $type=0, $info_bits=0, $notrigger=false, $date_start=null, $date_end=null, $array_options=0, $fk_unit=null, $pu_ht_devise=0, $origin='', $origin_id=0, $rang=-1, $special_code=0)
Add order line.
calcAndSetStatusDispatch(User $user, $closeopenorder=1, $comment='')
Calc status regarding to dispatched stock.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $price_base_type='HT', $info_bits=0, $type=0, $notrigger=0, $date_start='', $date_end='', $array_options=0, $fk_unit=null, $pu_ht_devise=0, $ref_supplier='')
Update line.
set_id_projet($user, $id_projet, $notrigger=0)
Set the id projet.
showDelay()
Show the customer delayed info.
getTooltipContentArray($params)
getTooltipContentArray
approve($user, $idwarehouse=0, $secondlevel=0)
Approve a supplier order.
Cancel($user, $idwarehouse=-1)
Cancel an approved order.
dispatchProduct($user, $product, $qty, $entrepot, $price=0, $comment='', $eatby='', $sellby='', $batch='', $fk_commandefourndet=0, $notrigger=0, $fk_reception=0)
Save a receiving into the tracking table of receiving (commande_fournisseur_dispatch) and add product...
valid($user, $idwarehouse=0, $notrigger=0)
Validate an order.
create($user, $notrigger=0)
Create order with draft status.
update(User $user, $notrigger=0)
Update Supplier Order.
createFromClone(User $user, $socid=0, $notrigger=0)
Load an object from its id and create a new one in database.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
const STATUS_ORDERSENT
Order sent, shipment on process.
commande($user, $date, $methode, $comment='')
Submit a supplier order to supplier.
getRights()
Returns the rights used for this class.
load_board($user, $mode='opened')
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getMaxDeliveryTimeDay($langs)
Return the max number delivery delay in day.
fetch($id, $ref='')
Get object and lines from database.
Livraison($user, $date, $type, $comment)
Set a delivery in database for this supplier order.
getDispachedLines($status=-1)
Return array of dispatched lines waiting to be approved for this order.
classifyBilled(User $user)
Class invoiced the supplier order.
initAsSpecimen()
Initialise an instance with random values.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
const SOURCE_ID_REPLENISHMENT
The constant used into source field to track the order was generated by the replenishement feature.
setStatus($user, $status)
Tag order with a particular status.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLibStatut($mode=0)
Return label of the status of object.
get_methodes_commande()
Get list of order methods.
hasDelay()
Is the supplier order delayed? We suppose a purchase ordered as late if a the purchase order has been...
LibStatut($status, $mode=0, $billed=0)
Return label of a status.
Class to manage line orders.
update($notrigger=0)
Update the line object into db.
insert($notrigger=0)
Insert line into database.
Class to manage customers orders.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Superclass for orders classes.
Superclass for orders classes.
Class to manage Dolibarr database access.
Class to manage stock movements.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('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') && $user->hasRight('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)) $sql
Social contributions to pay.
Definition: index.php:746
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1507
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1356
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
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...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setEventMessage($mesgs, $style='mesgs', $noduplicate=0)
Set event message in dol_events session object.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
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