dolibarr  19.0.0-dev
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 
256  public $fields = array(
257  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>10),
258  'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>25, 'searchall'=>1),
259  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>35),
260  'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefOrderSupplierShort', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'searchall'=>1),
261  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>45),
262  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>710),
263  'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>720),
264  'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>725),
265  'date_commande' =>array('type'=>'date', 'label'=>'OrderDateShort', 'enabled'=>1, 'visible'=>1, 'position'=>70),
266  'date_livraison' =>array('type'=>'datetime', 'label'=>'DeliveryDate', 'enabled'=>'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible'=>1, 'position'=>74),
267  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>41),
268  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>3, 'notnull'=>-1, 'position'=>80),
269  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>711),
270  'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>721),
271  'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>726),
272  'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>100),
273  'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>710),
274  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
275  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>1, 'position'=>135, 'isameasure'=>1),
276  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LT1', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
277  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LT2', 'enabled'=>1, 'visible'=>3, 'position'=>145, 'isameasure'=>1),
278  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
279  'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>750, 'searchall'=>1),
280  'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>760, 'searchall'=>1),
281  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>165),
282  'fk_input_method' =>array('type'=>'integer', 'label'=>'OrderMode', 'enabled'=>1, 'visible'=>3, 'position'=>170),
283  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>3, 'position'=>175),
284  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>3, 'position'=>180),
285  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>0, 'position'=>190),
286  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'isModEnabled("banque")', 'visible'=>3, 'position'=>200),
287  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>3, 'position'=>205),
288  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>3, 'position'=>210),
289  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>0, 'position'=>215),
290  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Currency', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>220),
291  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'CurrencyRate', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>225),
292  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>230),
293  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>235),
294  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
295  'date_creation' =>array('type'=>'datetime', 'label'=>'Date creation', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
296  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>1, 'notnull'=>1, 'position'=>50),
297  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1000, 'index'=>1),
298  'tms'=>array('type'=>'datetime', 'label'=>"DateModificationShort", 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
299  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'position'=>700),
300  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>701),
301  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>0, 'position'=>900),
302  );
303 
304 
308  const STATUS_DRAFT = 0;
309 
313  const STATUS_VALIDATED = 1;
314 
318  const STATUS_ACCEPTED = 2;
319 
323  const STATUS_ORDERSENT = 3;
324 
329 
334 
338  const STATUS_CANCELED = 6;
339 
344 
348  const STATUS_REFUSED = 9;
349 
350 
355 
356 
357 
363  public function __construct($db)
364  {
365  $this->db = $db;
366  }
367 
368 
376  public function fetch($id, $ref = '')
377  {
378  global $conf;
379 
380  // Check parameters
381  if (empty($id) && empty($ref)) {
382  return -1;
383  }
384 
385  $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,";
386  $sql .= " c.localtax1, c.localtax2, ";
387  $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
388  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_approve, c.fk_user_approve2,";
389  $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,";
390  $sql .= " c.fk_account,";
391  $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
392  $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
393  $sql .= " cm.libelle as methode_commande,";
394  $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
395  $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
396  $sql .= ', c.fk_incoterms, c.location_incoterms';
397  $sql .= ', i.libelle as label_incoterms';
398  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
399  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
400  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON c.fk_mode_reglement = p.id";
401  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_input_method as cm ON cm.rowid = c.fk_input_method";
402  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
403 
404  if (empty($id)) {
405  $sql .= " WHERE c.entity IN (".getEntity('supplier_order').")";
406  } else {
407  $sql .= " WHERE c.rowid=".((int) $id);
408  }
409 
410  if ($ref) {
411  $sql .= " AND c.ref='".$this->db->escape($ref)."'";
412  }
413 
414  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
415  $resql = $this->db->query($sql);
416  if ($resql) {
417  $obj = $this->db->fetch_object($resql);
418  if (!$obj) {
419  $this->error = 'Bill with id '.$id.' not found';
420  dol_syslog(get_class($this).'::fetch '.$this->error);
421  return 0;
422  }
423 
424  $this->id = $obj->rowid;
425  $this->entity = $obj->entity;
426 
427  $this->ref = $obj->ref;
428  $this->ref_supplier = $obj->ref_supplier;
429  $this->socid = $obj->fk_soc;
430  $this->fourn_id = $obj->fk_soc;
431  $this->statut = $obj->fk_statut;
432  $this->status = $obj->fk_statut;
433  $this->billed = $obj->billed;
434  $this->user_author_id = $obj->fk_user_author;
435  $this->user_valid_id = $obj->fk_user_valid;
436  $this->user_approve_id = $obj->fk_user_approve;
437  $this->user_approve_id2 = $obj->fk_user_approve2;
438  $this->total_ht = $obj->total_ht;
439  $this->total_tva = $obj->total_tva;
440  $this->total_localtax1 = $obj->localtax1;
441  $this->total_localtax2 = $obj->localtax2;
442  $this->total_ttc = $obj->total_ttc;
443  $this->date_creation = $this->db->jdate($obj->date_creation);
444  $this->date_valid = $this->db->jdate($obj->date_valid);
445  $this->date_approve = $this->db->jdate($obj->date_approve);
446  $this->date_approve2 = $this->db->jdate($obj->date_approve2);
447  $this->date_commande = $this->db->jdate($obj->date_commande); // date we make the order to supplier
448  if (isset($obj->date_commande)) {
449  $this->date = $this->date_commande;
450  } else {
451  $this->date = $this->date_creation;
452  }
453  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
454  $this->delivery_date = $this->db->jdate($obj->delivery_date);
455  $this->remise_percent = $obj->remise_percent;
456  $this->methode_commande_id = $obj->fk_input_method;
457  $this->methode_commande = $obj->methode_commande;
458 
459  $this->source = $obj->source;
460  $this->fk_project = $obj->fk_project;
461  $this->cond_reglement_id = $obj->fk_cond_reglement;
462  $this->cond_reglement_code = $obj->cond_reglement_code;
463  $this->cond_reglement = $obj->cond_reglement_label; // deprecated
464  $this->cond_reglement_label = $obj->cond_reglement_label;
465  $this->cond_reglement_doc = $obj->cond_reglement_doc;
466  $this->fk_account = $obj->fk_account;
467  $this->mode_reglement_id = $obj->fk_mode_reglement;
468  $this->mode_reglement_code = $obj->mode_reglement_code;
469  $this->mode_reglement = $obj->mode_reglement_libelle;
470  $this->note = $obj->note_private; // deprecated
471  $this->note_private = $obj->note_private;
472  $this->note_public = $obj->note_public;
473  $this->model_pdf = $obj->model_pdf;
474  $this->modelpdf = $obj->model_pdf; // deprecated
475 
476  //Incoterms
477  $this->fk_incoterms = $obj->fk_incoterms;
478  $this->location_incoterms = $obj->location_incoterms;
479  $this->label_incoterms = $obj->label_incoterms;
480 
481  // Multicurrency
482  $this->fk_multicurrency = $obj->fk_multicurrency;
483  $this->multicurrency_code = $obj->multicurrency_code;
484  $this->multicurrency_tx = $obj->multicurrency_tx;
485  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
486  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
487  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
488 
489  $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
490 
491  $this->db->free($resql);
492 
493  // Retrieve all extrafield
494  // fetch optionals attributes and labels
495  $this->fetch_optionals();
496 
497  if ($this->statut == 0) {
498  $this->brouillon = 1;
499  }
500 
501  /*
502  * Lines
503  */
504  $result = $this->fetch_lines();
505 
506  if ($result < 0) {
507  return -1;
508  } else {
509  return 1;
510  }
511  } else {
512  $this->error = $this->db->error()." sql=".$sql;
513  return -1;
514  }
515  }
516 
517  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
524  public function fetch_lines($only_product = 0)
525  {
526  global $conf;
527  // phpcs:enable
528 
529  $this->lines = array();
530 
531  $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
532  $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
533  $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
534  $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.special_code, l.fk_parent_line, l.rang,";
535  $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,";
536  $sql .= " l.fk_unit,";
537  $sql .= " l.date_start, l.date_end,";
538  $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
539  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
540  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
541  $sql .= " WHERE l.fk_commande = ".((int) $this->id);
542  if ($only_product) {
543  $sql .= ' AND p.fk_product_type = 0';
544  }
545  $sql .= " ORDER BY l.rang, l.rowid";
546  //print $sql;
547 
548  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
549 
550  $result = $this->db->query($sql);
551  if ($result) {
552  $num = $this->db->num_rows($result);
553  $i = 0;
554 
555  while ($i < $num) {
556  $objp = $this->db->fetch_object($result);
557 
558  $line = new CommandeFournisseurLigne($this->db);
559 
560  $line->id = $objp->rowid;
561  $line->fk_commande = $objp->fk_commande;
562  $line->desc = $objp->description;
563  $line->description = $objp->description;
564  $line->qty = $objp->qty;
565  $line->tva_tx = $objp->tva_tx;
566  $line->localtax1_tx = $objp->localtax1_tx;
567  $line->localtax2_tx = $objp->localtax2_tx;
568  $line->localtax1_type = $objp->localtax1_type;
569  $line->localtax2_type = $objp->localtax2_type;
570  $line->subprice = $objp->subprice;
571  $line->pu_ht = $objp->subprice;
572  $line->remise_percent = $objp->remise_percent;
573 
574  $line->vat_src_code = $objp->vat_src_code;
575  $line->total_ht = $objp->total_ht;
576  $line->total_tva = $objp->total_tva;
577  $line->total_localtax1 = $objp->total_localtax1;
578  $line->total_localtax2 = $objp->total_localtax2;
579  $line->total_ttc = $objp->total_ttc;
580  $line->product_type = $objp->product_type;
581 
582  $line->fk_product = $objp->fk_product;
583 
584  $line->libelle = $objp->product_label; // deprecated
585  $line->product_label = $objp->product_label;
586  $line->product_desc = $objp->product_desc;
587  $line->product_tobatch = $objp->product_tobatch;
588  $line->product_barcode = $objp->product_barcode;
589 
590  $line->ref = $objp->product_ref; // Ref of product
591  $line->product_ref = $objp->product_ref; // Ref of product
592  $line->ref_fourn = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
593  $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
594 
595  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
596  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
597  // Move this into another method and call it when required.
598 
599  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
600  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
601  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
602  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
603  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
604  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
605  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
606  $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid);
607  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
608  $sqlsearchpackage .= " LIMIT 1";
609 
610  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
611  if ($resqlsearchpackage) {
612  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
613  if ($objsearchpackage) {
614  $line->fk_fournprice = $objsearchpackage->rowid;
615  $line->packaging = $objsearchpackage->packaging;
616  }
617  } else {
618  $this->error = $this->db->lasterror();
619  return -1;
620  }
621  }
622 
623  $line->date_start = $this->db->jdate($objp->date_start);
624  $line->date_end = $this->db->jdate($objp->date_end);
625  $line->fk_unit = $objp->fk_unit;
626 
627  // Multicurrency
628  $line->fk_multicurrency = $objp->fk_multicurrency;
629  $line->multicurrency_code = $objp->multicurrency_code;
630  $line->multicurrency_subprice = $objp->multicurrency_subprice;
631  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
632  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
633  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
634 
635  $line->special_code = $objp->special_code;
636  $line->fk_parent_line = $objp->fk_parent_line;
637 
638  $line->rang = $objp->rang;
639 
640  // Retrieve all extrafield
641  // fetch optionals attributes and labels
642  $line->fetch_optionals();
643 
644  $this->lines[$i] = $line;
645 
646  $i++;
647  }
648  $this->db->free($result);
649 
650  return $num;
651  } else {
652  $this->error = $this->db->error()." sql=".$sql;
653  return -1;
654  }
655  }
656 
665  public function valid($user, $idwarehouse = 0, $notrigger = 0)
666  {
667  global $langs, $conf;
668  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
669 
670  $error = 0;
671 
672  dol_syslog(get_class($this)."::valid");
673  $result = 0;
674  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
675  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))) {
676  $this->db->begin();
677 
678  // Definition of supplier order numbering model name
679  $soc = new Societe($this->db);
680  $soc->fetch($this->fourn_id);
681 
682  // Check if object has a temporary ref
683  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
684  $num = $this->getNextNumRef($soc);
685  } else {
686  $num = $this->ref;
687  }
688  $this->newref = dol_sanitizeFileName($num);
689 
690  $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
691  $sql .= " SET ref='".$this->db->escape($num)."',";
692  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
693  $sql .= " date_valid='".$this->db->idate(dol_now())."',";
694  $sql .= " fk_user_valid = ".((int) $user->id);
695  $sql .= " WHERE rowid = ".((int) $this->id);
696  $sql .= " AND fk_statut = ".self::STATUS_DRAFT;
697 
698  $resql = $this->db->query($sql);
699  if (!$resql) {
700  dol_print_error($this->db);
701  $error++;
702  }
703 
704  if (!$error && !$notrigger) {
705  // Call trigger
706  $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
707  if ($result < 0) {
708  $error++;
709  }
710  // End call triggers
711  }
712 
713  if (!$error) {
714  $this->oldref = $this->ref;
715 
716  // Rename directory if dir was a temporary ref
717  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
718  // Now we rename also files into index
719  $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)."'";
720  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
721  $resql = $this->db->query($sql);
722  if (!$resql) {
723  $error++; $this->error = $this->db->lasterror();
724  }
725 
726  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
727  $oldref = dol_sanitizeFileName($this->ref);
728  $newref = dol_sanitizeFileName($num);
729  $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
730  $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
731  if (!$error && file_exists($dirsource)) {
732  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
733 
734  if (@rename($dirsource, $dirdest)) {
735  dol_syslog("Rename ok");
736  // Rename docs starting with $oldref with $newref
737  $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
738  foreach ($listoffiles as $fileentry) {
739  $dirsource = $fileentry['name'];
740  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
741  $dirsource = $fileentry['path'].'/'.$dirsource;
742  $dirdest = $fileentry['path'].'/'.$dirdest;
743  @rename($dirsource, $dirdest);
744  }
745  }
746  }
747  }
748  }
749 
750  if (!$error) {
751  $result = 1;
752  $this->statut = self::STATUS_VALIDATED;
753  $this->ref = $num;
754  }
755 
756  if (!$error) {
757  $this->db->commit();
758  return 1;
759  } else {
760  $this->db->rollback();
761  return -1;
762  }
763  } else {
764  $this->error = 'NotAuthorized';
765  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
766  return -1;
767  }
768  }
769 
776  public function getLibStatut($mode = 0)
777  {
778  return $this->LibStatut($this->statut, $mode, $this->billed);
779  }
780 
781  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
790  public function LibStatut($status, $mode = 0, $billed = 0)
791  {
792  // phpcs:enable
793  global $conf, $langs, $hookmanager;
794 
795  if (empty($this->statuts) || empty($this->statuts_short)) {
796  $langs->load('orders');
797 
798  $this->statuts[0] = 'StatusSupplierOrderDraft';
799  $this->statuts[1] = 'StatusSupplierOrderValidated';
800  $this->statuts[2] = 'StatusSupplierOrderApproved';
801  if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
802  $this->statuts[3] = 'StatusSupplierOrderOnProcess';
803  } else {
804  $this->statuts[3] = 'StatusSupplierOrderOnProcessWithValidation';
805  }
806  $this->statuts[4] = 'StatusSupplierOrderReceivedPartially';
807  $this->statuts[5] = 'StatusSupplierOrderReceivedAll';
808  $this->statuts[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
809  $this->statuts[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
810  $this->statuts[9] = 'StatusSupplierOrderRefused';
811 
812  // List of language codes for status
813  $this->statuts_short[0] = 'StatusSupplierOrderDraftShort';
814  $this->statuts_short[1] = 'StatusSupplierOrderValidatedShort';
815  $this->statuts_short[2] = 'StatusSupplierOrderApprovedShort';
816  $this->statuts_short[3] = 'StatusSupplierOrderOnProcessShort';
817  $this->statuts_short[4] = 'StatusSupplierOrderReceivedPartiallyShort';
818  $this->statuts_short[5] = 'StatusSupplierOrderReceivedAllShort';
819  $this->statuts_short[6] = 'StatusSupplierOrderCanceledShort';
820  $this->statuts_short[7] = 'StatusSupplierOrderCanceledShort';
821  $this->statuts_short[9] = 'StatusSupplierOrderRefusedShort';
822  }
823 
824  $statustrans = array(
825  0 => 'status0',
826  1 => 'status1b',
827  2 => 'status1',
828  3 => 'status4',
829  4 => 'status4b',
830  5 => 'status6',
831  6 => 'status9',
832  7 => 'status9',
833  9 => 'status9',
834  );
835 
836  $statusClass = 'status0';
837  if (!empty($statustrans[$status])) {
838  $statusClass = $statustrans[$status];
839  }
840 
841  $billedtext = '';
842  if ($billed) {
843  $billedtext = ' - '.$langs->trans("Billed");
844  }
845  if ($status == 5 && $billed) {
846  $statusClass = 'status6';
847  }
848 
849  $statusLong = $langs->transnoentitiesnoconv($this->statuts[$status]).$billedtext;
850  $statusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
851 
852  $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
853  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
854  if ($reshook > 0) {
855  return $hookmanager->resPrint;
856  }
857 
858  return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
859  }
860 
868  public function getTooltipContentArray($params)
869  {
870  global $conf, $langs, $user;
871 
872  $langs->loadLangs(['bills', 'orders']);
873 
874  $datas = [];
875  $nofetch = !empty($params['nofetch']);
876 
877  if ($user->hasRight("fournisseur", "commande", "read")) {
878  $datas['picto'] = '<u class="paddingrightonly">'.$langs->trans("SupplierOrder").'</u>';
879  if (isset($this->statut)) {
880  $datas['picto'] .= ' '.$this->getLibStatut(5);
881  }
882  if (!empty($this->ref)) {
883  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
884  }
885  if (!empty($this->ref_supplier)) {
886  $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
887  }
888  if (!$nofetch) {
889  $langs->load('companies');
890  if (empty($this->thirdparty)) {
891  $this->fetch_thirdparty();
892  }
893  $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
894  }
895  if (!empty($this->total_ht)) {
896  $datas['totalht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
897  }
898  if (!empty($this->total_tva)) {
899  $datas['totaltva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
900  }
901  if (!empty($this->total_ttc)) {
902  $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
903  }
904  if (!empty($this->date)) {
905  $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
906  }
907  if (!empty($this->delivery_date)) {
908  $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
909  }
910  }
911  return $datas;
912  }
913 
924  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
925  {
926  global $langs, $conf, $user, $hookmanager;
927 
928  $result = '';
929  $params = [
930  'id' => $this->id,
931  'objecttype' => $this->element,
932  'option' => $option,
933  'nofetch' => 1
934  ];
935  $classfortooltip = 'classfortooltip';
936  $dataparams = '';
937  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
938  $classfortooltip = 'classforajaxtooltip';
939  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
940  $label = '';
941  } else {
942  $label = implode($this->getTooltipContentArray($params));
943  }
944 
945  $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
946 
947  if ($option !== 'nolink') {
948  // Add param to save lastsearch_values or not
949  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
950  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
951  $add_save_lastsearch_values = 1;
952  }
953  if ($add_save_lastsearch_values) {
954  $url .= '&save_lastsearch_values=1';
955  }
956  }
957 
958  $linkclose = '';
959  if (empty($notooltip)) {
960  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
961  $label = $langs->trans("ShowOrder");
962  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
963  }
964  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
965  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
966  }
967 
968  $linkstart = '<a href="'.$url.'"';
969  $linkstart .= $linkclose.'>';
970  $linkend = '</a>';
971 
972  $result .= $linkstart;
973  if ($withpicto) {
974  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1);
975  }
976  if ($withpicto != 2) {
977  $result .= $this->ref;
978  }
979  $result .= $linkend;
980 
981  if ($addlinktonotes) {
982  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
983  if ($txttoshow) {
984  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
985  $result .= ' <span class="note inline-block">';
986  $result .= '<a href="'.DOL_URL_ROOT.'/fourn/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
987  $result .= img_picto('', 'note');
988  $result .= '</a>';
989  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
990  //$result.='</a>';
991  $result .= '</span>';
992  }
993  }
994 
995  global $action;
996  $hookmanager->initHooks(array($this->element . 'dao'));
997  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
998  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
999  if ($reshook > 0) {
1000  $result = $hookmanager->resPrint;
1001  } else {
1002  $result .= $hookmanager->resPrint;
1003  }
1004  return $result;
1005  }
1006 
1007 
1015  public function getNextNumRef($soc)
1016  {
1017  global $db, $langs, $conf;
1018  $langs->load("orders");
1019 
1020  if (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER)) {
1021  $mybool = false;
1022 
1023  $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER').'.php';
1024  $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1025 
1026  // Include file with class
1027  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1028 
1029  foreach ($dirmodels as $reldir) {
1030  $dir = dol_buildpath($reldir."core/modules/supplier_order/");
1031 
1032  // Load file with numbering class (if found)
1033  $mybool |= @include_once $dir.$file;
1034  }
1035 
1036  if ($mybool === false) {
1037  dol_print_error('', "Failed to include file ".$file);
1038  return '';
1039  }
1040 
1041  $obj = new $classname();
1042  $numref = $obj->getNextValue($soc, $this);
1043 
1044  if ($numref != "") {
1045  return $numref;
1046  } else {
1047  $this->error = $obj->error;
1048  return -1;
1049  }
1050  } else {
1051  $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1052  return -2;
1053  }
1054  }
1061  public function classifyBilled(User $user)
1062  {
1063  $error = 0;
1064 
1065  if ($this->billed) {
1066  return 0;
1067  }
1068 
1069  $this->db->begin();
1070 
1071  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
1072  $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > '.self::STATUS_DRAFT;
1073 
1074  if ($this->db->query($sql)) {
1075  if (!$error) {
1076  // Call trigger
1077  $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1078  if ($result < 0) {
1079  $error++;
1080  }
1081  // End call triggers
1082  }
1083 
1084  if (!$error) {
1085  $this->billed = 1;
1086 
1087  $this->db->commit();
1088  return 1;
1089  } else {
1090  $this->db->rollback();
1091  return -1;
1092  }
1093  } else {
1094  dol_print_error($this->db);
1095 
1096  $this->db->rollback();
1097  return -1;
1098  }
1099  }
1100 
1109  public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1110  {
1111  global $langs, $conf;
1112  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1113 
1114  $error = 0;
1115 
1116  dol_syslog(get_class($this)."::approve");
1117 
1118  if ($user->hasRight("fournisseur", "commande", "approuver")) {
1119  $now = dol_now();
1120 
1121  $this->db->begin();
1122 
1123  // Definition of order numbering model name
1124  $soc = new Societe($this->db);
1125  $soc->fetch($this->fourn_id);
1126 
1127  // Check if object has a temporary ref
1128  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1129  $num = $this->getNextNumRef($soc);
1130  } else {
1131  $num = $this->ref;
1132  }
1133  $this->newref = dol_sanitizeFileName($num);
1134 
1135  // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1136  $movetoapprovestatus = true;
1137  $comment = '';
1138 
1139  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1140  $sql .= " SET ref='".$this->db->escape($num)."',";
1141  if (empty($secondlevel)) { // standard or first level approval
1142  $sql .= " date_approve='".$this->db->idate($now)."',";
1143  $sql .= " fk_user_approve = ".$user->id;
1144  if (!empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1145  if (empty($this->user_approve_id2)) {
1146  $movetoapprovestatus = false; // second level approval not done
1147  $comment = ' (first level)';
1148  }
1149  }
1150  } else // request a second level approval
1151  {
1152  $sql .= " date_approve2='".$this->db->idate($now)."',";
1153  $sql .= " fk_user_approve2 = ".((int) $user->id);
1154  if (empty($this->user_approve_id)) {
1155  $movetoapprovestatus = false; // first level approval not done
1156  }
1157  $comment = ' (second level)';
1158  }
1159  // If double approval is required and first approval, we keep status to 1 = validated
1160  if ($movetoapprovestatus) {
1161  $sql .= ", fk_statut = ".self::STATUS_ACCEPTED;
1162  } else {
1163  $sql .= ", fk_statut = ".self::STATUS_VALIDATED;
1164  }
1165  $sql .= " WHERE rowid = ".((int) $this->id);
1166  $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1167 
1168  if ($this->db->query($sql)) {
1169  if (!empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT)) {
1170  $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1171  if ($result < 0 && $result != -2) { // -2 means already exists
1172  $error++;
1173  }
1174  }
1175 
1176  // If stock is incremented on validate order, we must increment it
1177  if (!$error && $movetoapprovestatus && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) {
1178  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1179  $langs->load("agenda");
1180 
1181  $cpt = count($this->lines);
1182  for ($i = 0; $i < $cpt; $i++) {
1183  // Product with reference
1184  if ($this->lines[$i]->fk_product > 0) {
1185  $this->line = $this->lines[$i];
1186  $mouvP = new MouvementStock($this->db);
1187  $mouvP->origin = &$this;
1188  $mouvP->setOrigin($this->element, $this->id);
1189  // We decrement stock of product (and sub-products)
1190  $up_ht_disc = $this->lines[$i]->subprice;
1191  if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
1192  $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1193  }
1194  $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1195  if ($result < 0) {
1196  $error++;
1197  }
1198  unset($this->line);
1199  }
1200  }
1201  }
1202 
1203  if (!$error) {
1204  // Call trigger
1205  $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1206  if ($result < 0) {
1207  $error++;
1208  }
1209  // End call triggers
1210  }
1211 
1212  if (!$error) {
1213  $this->ref = $this->newref;
1214 
1215  if ($movetoapprovestatus) {
1216  $this->statut = self::STATUS_ACCEPTED;
1217  } else {
1218  $this->statut = self::STATUS_VALIDATED;
1219  }
1220  if (empty($secondlevel)) { // standard or first level approval
1221  $this->date_approve = $now;
1222  $this->user_approve_id = $user->id;
1223  } else // request a second level approval
1224  {
1225  $this->date_approve2 = $now;
1226  $this->user_approve_id2 = $user->id;
1227  }
1228 
1229  $this->db->commit();
1230  return 1;
1231  } else {
1232  $this->db->rollback();
1233  return -1;
1234  }
1235  } else {
1236  $this->db->rollback();
1237  $this->error = $this->db->lasterror();
1238  return -1;
1239  }
1240  } else {
1241  dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1242  }
1243  return -1;
1244  }
1245 
1252  public function refuse($user)
1253  {
1254  global $conf, $langs;
1255 
1256  $error = 0;
1257 
1258  dol_syslog(get_class($this)."::refuse");
1259  $result = 0;
1260  if ($user->hasRight("fournisseur", "commande", "approuver")) {
1261  $this->db->begin();
1262 
1263  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1264  $sql .= " WHERE rowid = ".((int) $this->id);
1265 
1266  if ($this->db->query($sql)) {
1267  $result = 0;
1268 
1269  if ($error == 0) {
1270  // Call trigger
1271  $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1272  if ($result < 0) {
1273  $error++;
1274  $this->db->rollback();
1275  } else {
1276  $this->db->commit();
1277  }
1278  // End call triggers
1279  }
1280  } else {
1281  $this->db->rollback();
1282  $this->error = $this->db->lasterror();
1283  dol_syslog(get_class($this)."::refuse Error -1");
1284  $result = -1;
1285  }
1286  } else {
1287  dol_syslog(get_class($this)."::refuse Not Authorized");
1288  }
1289  return $result;
1290  }
1291 
1292  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1301  public function Cancel($user, $idwarehouse = -1)
1302  {
1303  // phpcs:enable
1304  global $langs, $conf;
1305 
1306  $error = 0;
1307 
1308  //dol_syslog("CommandeFournisseur::Cancel");
1309  $result = 0;
1310  if ($user->hasRight("fournisseur", "commande", "commander")) {
1311  $statut = self::STATUS_CANCELED;
1312 
1313  $this->db->begin();
1314 
1315  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".((int) $statut);
1316  $sql .= " WHERE rowid = ".((int) $this->id);
1317  dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1318  if ($this->db->query($sql)) {
1319  $result = 0;
1320 
1321  // Call trigger
1322  $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1323  if ($result < 0) {
1324  $error++;
1325  }
1326  // End call triggers
1327 
1328  if ($error == 0) {
1329  $this->db->commit();
1330  return 1;
1331  } else {
1332  $this->db->rollback();
1333  return -1;
1334  }
1335  } else {
1336  $this->db->rollback();
1337  $this->error = $this->db->lasterror();
1338  dol_syslog(get_class($this)."::cancel ".$this->error);
1339  return -1;
1340  }
1341  } else {
1342  dol_syslog(get_class($this)."::cancel Not Authorized");
1343  return -1;
1344  }
1345  }
1346 
1356  public function commande($user, $date, $methode, $comment = '')
1357  {
1358  global $langs;
1359  dol_syslog(get_class($this)."::commande");
1360  $error = 0;
1361  if ($user->hasRight("fournisseur", "commande", "commander")) {
1362  $this->db->begin();
1363 
1364  $newnoteprivate = $this->note_private;
1365  if ($comment) {
1366  $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment").': '.$comment);
1367  }
1368 
1369  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1370  $sql .= " SET fk_statut=".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."', ";
1371  $sql .= " note_private='".$this->db->escape($newnoteprivate)."'";
1372  $sql .= " WHERE rowid=".((int) $this->id);
1373 
1374  dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1375  if ($this->db->query($sql)) {
1376  $this->statut = self::STATUS_ORDERSENT;
1377  $this->methode_commande_id = $methode;
1378  $this->date_commande = $date;
1379  $this->context = array('comments' => $comment);
1380 
1381  // Call trigger
1382  $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1383  if ($result < 0) {
1384  $error++;
1385  }
1386  // End call triggers
1387  } else {
1388  $error++;
1389  $this->error = $this->db->lasterror();
1390  $this->errors[] = $this->db->lasterror();
1391  }
1392 
1393  if (!$error) {
1394  $this->db->commit();
1395  } else {
1396  $this->db->rollback();
1397  }
1398  } else {
1399  $error++;
1400  $this->error = $langs->trans('NotAuthorized');
1401  $this->errors[] = $langs->trans('NotAuthorized');
1402  dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1403  }
1404 
1405  return ($error ? -1 : 1);
1406  }
1407 
1415  public function create($user, $notrigger = 0)
1416  {
1417  global $langs, $conf, $hookmanager;
1418 
1419  $this->db->begin();
1420 
1421  $error = 0;
1422  $now = dol_now();
1423 
1424  // set tmp vars
1425  $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1426  if (empty($date)) {
1427  $date = $now;
1428  }
1429  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1430 
1431  // Clean parameters
1432  if (empty($this->source)) {
1433  $this->source = 0;
1434  }
1435 
1436  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1437  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1438  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1439  } else {
1440  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1441  }
1442  if (empty($this->fk_multicurrency)) {
1443  $this->multicurrency_code = $conf->currency;
1444  $this->fk_multicurrency = 0;
1445  $this->multicurrency_tx = 1;
1446  }
1447 
1448  // We set order into draft status
1449  $this->brouillon = 1;
1450 
1451  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1452  $sql .= "ref";
1453  $sql .= ", ref_supplier";
1454  $sql .= ", note_private";
1455  $sql .= ", note_public";
1456  $sql .= ", entity";
1457  $sql .= ", fk_soc";
1458  $sql .= ", fk_projet";
1459  $sql .= ", date_creation";
1460  $sql .= ", date_livraison";
1461  $sql .= ", fk_user_author";
1462  $sql .= ", fk_statut";
1463  $sql .= ", source";
1464  $sql .= ", model_pdf";
1465  $sql .= ", fk_mode_reglement";
1466  $sql .= ", fk_cond_reglement";
1467  $sql .= ", fk_account";
1468  $sql .= ", fk_incoterms, location_incoterms";
1469  $sql .= ", fk_multicurrency";
1470  $sql .= ", multicurrency_code";
1471  $sql .= ", multicurrency_tx";
1472  $sql .= ") ";
1473  $sql .= " VALUES (";
1474  $sql .= "'(PROV)'";
1475  $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
1476  $sql .= ", '".$this->db->escape($this->note_private)."'";
1477  $sql .= ", '".$this->db->escape($this->note_public)."'";
1478  $sql .= ", ".setEntity($this);
1479  $sql .= ", ".((int) $this->socid);
1480  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1481  $sql .= ", '".$this->db->idate($date)."'";
1482  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
1483  $sql .= ", ".((int) $user->id);
1484  $sql .= ", ".self::STATUS_DRAFT;
1485  $sql .= ", ".((int) $this->source);
1486  $sql .= ", '".$this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF'))."'";
1487  $sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1488  $sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1489  $sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
1490  $sql .= ", ".(int) $this->fk_incoterms;
1491  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1492  $sql .= ", ".(int) $this->fk_multicurrency;
1493  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1494  $sql .= ", ".(double) $this->multicurrency_tx;
1495  $sql .= ")";
1496 
1497  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1498  if ($this->db->query($sql)) {
1499  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1500 
1501  if ($this->id) {
1502  $num = count($this->lines);
1503 
1504  // insert products details into database
1505  for ($i = 0; $i < $num; $i++) {
1506  $line = $this->lines[$i];
1507  if (!is_object($line)) {
1508  $line = (object) $line;
1509  }
1510 
1511 
1512  //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1513 
1514  // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1515  $result = $this->addline(
1516  $line->desc,
1517  $line->subprice,
1518  $line->qty,
1519  $line->tva_tx,
1520  $line->localtax1_tx,
1521  $line->localtax2_tx,
1522  $line->fk_product,
1523  0,
1524  $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
1525  $line->remise_percent,
1526  'HT',
1527  0,
1528  $line->product_type,
1529  $line->info_bits,
1530  false,
1531  $line->date_start,
1532  $line->date_end,
1533  $line->array_options,
1534  $line->fk_unit,
1535  $line->special_code
1536  );
1537  if ($result < 0) {
1538  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functionnal error
1539  $this->db->rollback();
1540  return -1;
1541  }
1542  }
1543 
1544  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1545  $sql .= " SET ref='(PROV".$this->id.")'";
1546  $sql .= " WHERE rowid=".((int) $this->id);
1547  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1548  if ($this->db->query($sql)) {
1549  // Add link with price request and supplier order
1550  if ($this->id) {
1551  $this->ref = "(PROV".$this->id.")";
1552 
1553  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1554  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1555  }
1556 
1557  // Add object linked
1558  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1559  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1560  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, ...))
1561  foreach ($tmp_origin_id as $origin_id) {
1562  $ret = $this->add_object_linked($origin, $origin_id);
1563  if (!$ret) {
1564  dol_print_error($this->db);
1565  $error++;
1566  }
1567  }
1568  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1569  {
1570  $origin_id = $tmp_origin_id;
1571  $ret = $this->add_object_linked($origin, $origin_id);
1572  if (!$ret) {
1573  dol_print_error($this->db);
1574  $error++;
1575  }
1576  }
1577  }
1578  }
1579  }
1580 
1581  if (!$error) {
1582  $result = $this->insertExtraFields();
1583  if ($result < 0) {
1584  $error++;
1585  }
1586  }
1587 
1588  if (!$error && !$notrigger) {
1589  // Call trigger
1590  $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1591  if ($result < 0) {
1592  $this->db->rollback();
1593 
1594  return -1;
1595  }
1596  // End call triggers
1597  }
1598 
1599  $this->db->commit();
1600  return $this->id;
1601  } else {
1602  $this->error = $this->db->lasterror();
1603  $this->db->rollback();
1604 
1605  return -2;
1606  }
1607  } else {
1608  $this->error = 'Failed to get ID of inserted line';
1609 
1610  return -1;
1611  }
1612  } else {
1613  $this->error = $this->db->lasterror();
1614  $this->db->rollback();
1615 
1616  return -1;
1617  }
1618  }
1619 
1627  public function update(User $user, $notrigger = 0)
1628  {
1629  global $conf;
1630 
1631  $error = 0;
1632 
1633  // Clean parameters
1634  if (isset($this->ref)) {
1635  $this->ref = trim($this->ref);
1636  }
1637  if (isset($this->ref_supplier)) {
1638  $this->ref_supplier = trim($this->ref_supplier);
1639  }
1640  if (isset($this->note_private)) {
1641  $this->note_private = trim($this->note_private);
1642  }
1643  if (isset($this->note_public)) {
1644  $this->note_public = trim($this->note_public);
1645  }
1646  if (isset($this->model_pdf)) {
1647  $this->model_pdf = trim($this->model_pdf);
1648  }
1649  if (isset($this->import_key)) {
1650  $this->import_key = trim($this->import_key);
1651  }
1652 
1653  // Update request
1654  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1655 
1656  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1657  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1658  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1659  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1660  $sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
1661  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1662  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1663  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1664  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1665  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1666  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1667  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1668  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1669  $sql .= " fk_user_valid=".(isset($this->user_valid) && $this->user_valid > 0 ? $this->user_valid : "null").",";
1670  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1671  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1672  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1673  $sql .= " date_livraison=".(strval($this->delivery_date) != '' ? "'".$this->db->idate($this->delivery_date)."'" : 'null').",";
1674  //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1675  $sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
1676  //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1677  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1678  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1679  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1680  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1681 
1682  $sql .= " WHERE rowid=".((int) $this->id);
1683 
1684  $this->db->begin();
1685 
1686  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1687  $resql = $this->db->query($sql);
1688  if (!$resql) {
1689  $error++;
1690  $this->errors[] = "Error ".$this->db->lasterror();
1691  }
1692 
1693  if (!$error) {
1694  $result = $this->insertExtraFields();
1695  if ($result < 0) {
1696  $error++;
1697  }
1698  }
1699 
1700  if (!$error && !$notrigger) {
1701  // Call trigger
1702  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1703  if ($result < 0) {
1704  $error++;
1705  }
1706  // End call triggers
1707  }
1708 
1709  // Commit or rollback
1710  if ($error) {
1711  foreach ($this->errors as $errmsg) {
1712  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1713  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1714  }
1715  $this->db->rollback();
1716  return -1 * $error;
1717  } else {
1718  $this->db->commit();
1719  return 1;
1720  }
1721  }
1722 
1731  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1732  {
1733  global $conf, $user, $hookmanager;
1734 
1735  $error = 0;
1736 
1737  $this->db->begin();
1738 
1739  // get extrafields so they will be clone
1740  foreach ($this->lines as $line) {
1741  $line->fetch_optionals();
1742  }
1743 
1744  // Load source object
1745  $objFrom = clone $this;
1746 
1747  // Change socid if needed
1748  if (!empty($socid) && $socid != $this->socid) {
1749  $objsoc = new Societe($this->db);
1750 
1751  if ($objsoc->fetch($socid) > 0) {
1752  $this->socid = $objsoc->id;
1753  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1754  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1755  $this->fk_project = 0;
1756  $this->fk_delivery_address = 0;
1757  }
1758 
1759  // TODO Change product price if multi-prices
1760  }
1761 
1762  $this->id = 0;
1763  $this->statut = self::STATUS_DRAFT;
1764 
1765  // Clear fields
1766  $this->user_author_id = $user->id;
1767  $this->user_valid = 0;
1768  $this->date_creation = '';
1769  $this->date_validation = '';
1770  $this->ref_supplier = '';
1771  $this->user_approve_id = '';
1772  $this->user_approve_id2 = '';
1773  $this->date_approve = '';
1774  $this->date_approve2 = '';
1775 
1776  // Create clone
1777  $this->context['createfromclone'] = 'createfromclone';
1778  $result = $this->create($user, $notrigger);
1779  if ($result < 0) {
1780  $error++;
1781  }
1782 
1783  if (!$error) {
1784  // Hook of thirdparty module
1785  if (is_object($hookmanager)) {
1786  $parameters = array('objFrom'=>$objFrom);
1787  $action = '';
1788  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1789  if ($reshook < 0) {
1790  $this->setErrorsFromObject($hookmanager);
1791  $error++;
1792  }
1793  }
1794  }
1795 
1796  unset($this->context['createfromclone']);
1797 
1798  // End
1799  if (!$error) {
1800  $this->db->commit();
1801  return $this->id;
1802  } else {
1803  $this->db->rollback();
1804  return -1;
1805  }
1806  }
1807 
1837  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)
1838  {
1839  global $langs, $mysoc, $conf;
1840 
1841  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");
1842  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1843 
1844  if ($this->statut == self::STATUS_DRAFT) {
1845  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1846 
1847  // Clean parameters
1848  if (empty($qty)) {
1849  $qty = 0;
1850  }
1851  if (!$info_bits) {
1852  $info_bits = 0;
1853  }
1854  if (empty($txtva)) {
1855  $txtva = 0;
1856  }
1857  if (empty($rang)) {
1858  $rang = 0;
1859  }
1860  if (empty($txlocaltax1)) {
1861  $txlocaltax1 = 0;
1862  }
1863  if (empty($txlocaltax2)) {
1864  $txlocaltax2 = 0;
1865  }
1866  if (empty($remise_percent)) {
1867  $remise_percent = 0;
1868  }
1869 
1870  $remise_percent = price2num($remise_percent);
1871  $qty = price2num($qty);
1872  $pu_ht = price2num($pu_ht);
1873  $pu_ht_devise = price2num($pu_ht_devise);
1874  $pu_ttc = price2num($pu_ttc);
1875  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
1876  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1877  }
1878  $txlocaltax1 = price2num($txlocaltax1);
1879  $txlocaltax2 = price2num($txlocaltax2);
1880  if ($price_base_type == 'HT') {
1881  $pu = $pu_ht;
1882  } else {
1883  $pu = $pu_ttc;
1884  }
1885  $desc = trim($desc);
1886 
1887  // Check parameters
1888  if ($qty < 0 && !$fk_product) {
1889  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1890  return -1;
1891  }
1892  if ($type < 0) {
1893  return -1;
1894  }
1895  if ($date_start && $date_end && $date_start > $date_end) {
1896  $langs->load("errors");
1897  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1898  return -1;
1899  }
1900 
1901 
1902  $this->db->begin();
1903 
1904  $product_type = $type;
1905  $label = ''; // deprecated
1906 
1907  if ($fk_product > 0) {
1908  if (!empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY)) { // Not the common case
1909  // Check quantity is enough
1910  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);
1911  $prod = new ProductFournisseur($this->db);
1912  if ($prod->fetch($fk_product) > 0) {
1913  $product_type = $prod->type;
1914  $label = $prod->label;
1915 
1916  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1917  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1918  $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
1919 
1920  // If supplier order created from sales order, we take best supplier price
1921  // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1922  if ($result > 0 && ($origin == 'commande' || $pu === '')) {
1923  $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1924  $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1925  // is remise percent not keyed but present for the product we add it
1926  if ($remise_percent == 0 && $prod->remise_percent != 0) {
1927  $remise_percent = $prod->remise_percent;
1928  }
1929  }
1930  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
1931  $langs->load("errors");
1932  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1933  $this->db->rollback();
1934  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1935  //$pu = $prod->fourn_pu; // We do not overwrite unit price
1936  //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
1937  return -1;
1938  }
1939  if ($result == -1) {
1940  $langs->load("errors");
1941  $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1942  $this->db->rollback();
1943  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1944  return -1;
1945  }
1946  if ($result < -1) {
1947  $this->error = $prod->error;
1948  $this->db->rollback();
1949  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1950  return -1;
1951  }
1952  } else {
1953  $this->error = $prod->error;
1954  $this->db->rollback();
1955  return -1;
1956  }
1957  }
1958 
1959  // Predefine quantity according to packaging
1960  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
1961  $prod = new Product($this->db);
1962  $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid));
1963 
1964  if ($qty < $prod->packaging) {
1965  $qty = $prod->packaging;
1966  } else {
1967  if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
1968  $coeff = intval($qty / $prod->packaging) + 1;
1969  $qty = $prod->packaging * $coeff;
1970  setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
1971  }
1972  }
1973  }
1974  }
1975 
1976  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
1977  $pu = 0;
1978  }
1979 
1980  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1981 
1982  // Clean vat code
1983  $reg = array();
1984  $vat_src_code = '';
1985  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
1986  $vat_src_code = $reg[1];
1987  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
1988  }
1989 
1990  // Calcul du total TTC et de la TVA pour la ligne a partir de
1991  // qty, pu, remise_percent et txtva
1992  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1993  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1994 
1995  $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);
1996 
1997  $total_ht = $tabprice[0];
1998  $total_tva = $tabprice[1];
1999  $total_ttc = $tabprice[2];
2000  $total_localtax1 = $tabprice[9];
2001  $total_localtax2 = $tabprice[10];
2002  $pu = $pu_ht = $tabprice[3];
2003 
2004  // MultiCurrency
2005  $multicurrency_total_ht = $tabprice[16];
2006  $multicurrency_total_tva = $tabprice[17];
2007  $multicurrency_total_ttc = $tabprice[18];
2008  $pu_ht_devise = $tabprice[19];
2009 
2010  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2011  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2012 
2013  if ($rang < 0) {
2014  $rangmax = $this->line_max();
2015  $rang = $rangmax + 1;
2016  }
2017 
2018  // Insert line
2019  $this->line = new CommandeFournisseurLigne($this->db);
2020 
2021  $this->line->context = $this->context;
2022 
2023  $this->line->fk_commande = $this->id;
2024  $this->line->label = $label;
2025  $this->line->ref_fourn = $ref_supplier;
2026  $this->line->ref_supplier = $ref_supplier;
2027  $this->line->desc = $desc;
2028  $this->line->qty = $qty;
2029  $this->line->tva_tx = $txtva;
2030  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2031  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2032  $this->line->localtax1_type = $localtax1_type;
2033  $this->line->localtax2_type = $localtax2_type;
2034  $this->line->fk_product = $fk_product;
2035  $this->line->product_type = $product_type;
2036  $this->line->remise_percent = $remise_percent;
2037  $this->line->subprice = $pu_ht;
2038  $this->line->rang = $rang;
2039  $this->line->info_bits = $info_bits;
2040 
2041  $this->line->vat_src_code = $vat_src_code;
2042  $this->line->total_ht = $total_ht;
2043  $this->line->total_tva = $total_tva;
2044  $this->line->total_localtax1 = $total_localtax1;
2045  $this->line->total_localtax2 = $total_localtax2;
2046  $this->line->total_ttc = $total_ttc;
2047  $this->line->product_type = $type;
2048  $this->line->special_code = (!empty($this->special_code) ? $this->special_code : 0);
2049  $this->line->origin = $origin;
2050  $this->line->origin_id = $origin_id;
2051  $this->line->fk_unit = $fk_unit;
2052 
2053  $this->line->date_start = $date_start;
2054  $this->line->date_end = $date_end;
2055 
2056  // Multicurrency
2057  $this->line->fk_multicurrency = $this->fk_multicurrency;
2058  $this->line->multicurrency_code = $this->multicurrency_code;
2059  $this->line->multicurrency_subprice = $pu_ht_devise;
2060  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2061  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2062  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2063 
2064  $this->line->subprice = $pu_ht;
2065  $this->line->price = $this->line->subprice;
2066 
2067  $this->line->remise_percent = $remise_percent;
2068 
2069  if (is_array($array_options) && count($array_options) > 0) {
2070  $this->line->array_options = $array_options;
2071  }
2072 
2073  $result = $this->line->insert($notrigger);
2074  if ($result > 0) {
2075  // Reorder if child line
2076  if (!empty($fk_parent_line)) {
2077  $this->line_order(true, 'DESC');
2078  } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2079  $linecount = count($this->lines);
2080  for ($ii = $rang; $ii <= $linecount; $ii++) {
2081  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2082  }
2083  }
2084 
2085  // Mise a jour informations denormalisees au niveau de la commande meme
2086  $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.
2087  if ($result > 0) {
2088  $this->db->commit();
2089  return $this->line->id;
2090  } else {
2091  $this->db->rollback();
2092  return -1;
2093  }
2094  } else {
2095  $this->error = $this->line->error;
2096  $this->errors = $this->line->errors;
2097  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2098  $this->db->rollback();
2099  return -1;
2100  }
2101  }
2102  return -1;
2103  }
2104 
2105 
2123  public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2124  {
2125  global $conf, $langs;
2126 
2127  $error = 0;
2128  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2129 
2130  // Check parameters (if test are wrong here, there is bug into caller)
2131  if ($entrepot <= 0) {
2132  $this->error = 'ErrorBadValueForParameterWarehouse';
2133  return -1;
2134  }
2135  if ($qty == 0) {
2136  $this->error = 'ErrorBadValueForParameterQty';
2137  return -1;
2138  }
2139 
2140  $dispatchstatus = 1;
2141  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2142  $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2143  }
2144 
2145  $now = dol_now();
2146 
2147  $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2148 
2149  if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2150  $this->db->begin();
2151 
2152  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
2153  $sql .= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2154  $sql .= " ('".$this->id."','".$product."','".$qty."',".($entrepot > 0 ? "'".$entrepot."'" : "null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
2155  $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");
2156  $sql .= ")";
2157 
2158  dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
2159  $resql = $this->db->query($sql);
2160  if ($resql) {
2161  if (!$notrigger) {
2162  global $conf, $langs, $user;
2163  // Call trigger
2164  $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2165  if ($result < 0) {
2166  $error++;
2167  }
2168  // End call triggers
2169  }
2170  } else {
2171  $this->error = $this->db->lasterror();
2172  $error++;
2173  }
2174 
2175  // If module stock is enabled and the stock increase is done on purchase order dispatching
2176  if (!$error && $entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
2177  $mouv = new MouvementStock($this->db);
2178  if ($product > 0) {
2179  // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2180  $mouv->origin = &$this;
2181  $mouv->setOrigin($this->element, $this->id);
2182 
2183  // Method change if qty < 0
2184  if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qty < 0) {
2185  $result = $mouv->livraison($user, $product, $entrepot, $qty*(-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2186  } else {
2187  $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2188  }
2189 
2190  if ($result < 0) {
2191  $this->error = $mouv->error;
2192  $this->errors = $mouv->errors;
2193  dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
2194  $error++;
2195  }
2196  }
2197  }
2198 
2199  if ($error == 0) {
2200  $this->db->commit();
2201  return 1;
2202  } else {
2203  $this->db->rollback();
2204  return -1;
2205  }
2206  } else {
2207  $this->error = 'BadStatusForObject';
2208  return -2;
2209  }
2210  }
2211 
2219  public function deleteline($idline, $notrigger = 0)
2220  {
2221  if ($this->statut == 0) {
2222  $line = new CommandeFournisseurLigne($this->db);
2223 
2224  if ($line->fetch($idline) <= 0) {
2225  return 0;
2226  }
2227 
2228  // check if not yet received
2229  $dispatchedLines = $this->getDispachedLines();
2230  foreach ($dispatchedLines as $dispatchLine) {
2231  if ($dispatchLine['orderlineid'] == $idline) {
2232  $this->error = "LineAlreadyDispatched";
2233  $this->errors[] = $this->error;
2234  return -3;
2235  }
2236  }
2237 
2238  if ($line->delete($notrigger) > 0) {
2239  $this->update_price(1);
2240  return 1;
2241  } else {
2242  $this->error = $line->error;
2243  $this->errors = $line->errors;
2244  return -1;
2245  }
2246  } else {
2247  return -2;
2248  }
2249  }
2250 
2258  public function delete(User $user, $notrigger = 0)
2259  {
2260  global $langs, $conf;
2261  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2262 
2263  $error = 0;
2264 
2265  $this->db->begin();
2266 
2267  if (empty($notrigger)) {
2268  // Call trigger
2269  $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2270  if ($result < 0) {
2271  $this->errors[] = 'ErrorWhenRunningTrigger';
2272  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2273  $this->db->rollback();
2274  return -1;
2275  }
2276  // End call triggers
2277  }
2278 
2279  // Test we can delete
2280  $this->fetchObjectLinked(null, 'order_supplier');
2281  if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2282  foreach ($this->linkedObjects['reception'] as $element) {
2283  if ($element->statut >= 0) {
2284  $this->errors[] = $langs->trans('ReceptionExist');
2285  $error++;
2286  break;
2287  }
2288  }
2289  }
2290 
2291  $main = MAIN_DB_PREFIX.'commande_fournisseurdet';
2292  $ef = $main."_extrafields";
2293  $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".((int) $this->id).")";
2294  dol_syslog(get_class($this)."::delete extrafields lines", LOG_DEBUG);
2295  if (!$this->db->query($sql)) {
2296  $this->error = $this->db->lasterror();
2297  $this->errors[] = $this->db->lasterror();
2298  $error++;
2299  }
2300 
2301  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseurdet WHERE fk_commande =".((int) $this->id);
2302  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2303  if (!$this->db->query($sql)) {
2304  $this->error = $this->db->lasterror();
2305  $this->errors[] = $this->db->lasterror();
2306  $error++;
2307  }
2308 
2309  $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE rowid =".((int) $this->id);
2310  dol_syslog(get_class($this)."::delete", LOG_DEBUG);
2311  if ($resql = $this->db->query($sql)) {
2312  if ($this->db->affected_rows($resql) < 1) {
2313  $this->error = $this->db->lasterror();
2314  $this->errors[] = $this->db->lasterror();
2315  $error++;
2316  }
2317  } else {
2318  $this->error = $this->db->lasterror();
2319  $this->errors[] = $this->db->lasterror();
2320  $error++;
2321  }
2322 
2323  // Remove extrafields
2324  if (!$error) {
2325  $result = $this->deleteExtraFields();
2326  if ($result < 0) {
2327  $this->error = 'FailToDeleteExtraFields';
2328  $this->errors[] = 'FailToDeleteExtraFields';
2329  $error++;
2330  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
2331  }
2332  }
2333 
2334  // Delete linked object
2335  $res = $this->deleteObjectLinked();
2336  if ($res < 0) {
2337  $this->error = 'FailToDeleteObjectLinked';
2338  $this->errors[] = 'FailToDeleteObjectLinked';
2339  $error++;
2340  }
2341 
2342  if (!$error) {
2343  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2344  $this->deleteEcmFiles();
2345 
2346  // We remove directory
2347  $ref = dol_sanitizeFileName($this->ref);
2348  if ($conf->fournisseur->commande->dir_output) {
2349  $dir = $conf->fournisseur->commande->dir_output."/".$ref;
2350  $file = $dir."/".$ref.".pdf";
2351  if (file_exists($file)) {
2352  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2353  $this->error = 'ErrorFailToDeleteFile';
2354  $this->errors[] = 'ErrorFailToDeleteFile';
2355  $error++;
2356  }
2357  }
2358  if (file_exists($dir)) {
2359  $res = @dol_delete_dir_recursive($dir);
2360  if (!$res) {
2361  $this->error = 'ErrorFailToDeleteDir';
2362  $this->errors[] = 'ErrorFailToDeleteDir';
2363  $error++;
2364  }
2365  }
2366  }
2367  }
2368 
2369  if (!$error) {
2370  dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2371  $this->db->commit();
2372  return 1;
2373  } else {
2374  dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2375  $this->db->rollback();
2376  return -$error;
2377  }
2378  }
2379 
2380  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2386  public function get_methodes_commande()
2387  {
2388  // phpcs:enable
2389  $sql = "SELECT rowid, libelle";
2390  $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method";
2391  $sql .= " WHERE active = 1";
2392 
2393  $resql = $this->db->query($sql);
2394  if ($resql) {
2395  $i = 0;
2396  $num = $this->db->num_rows($resql);
2397  $this->methodes_commande = array();
2398  while ($i < $num) {
2399  $row = $this->db->fetch_row($resql);
2400 
2401  $this->methodes_commande[$row[0]] = $row[1];
2402 
2403  $i++;
2404  }
2405  return 0;
2406  } else {
2407  return -1;
2408  }
2409  }
2410 
2419  public function getDispachedLines($status = -1)
2420  {
2421  $ret = array();
2422 
2423  // List of already dispatched lines
2424  $sql = "SELECT p.ref, p.label,";
2425  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2426  $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_commandefourndet";
2427  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
2428  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2429  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2430  $sql .= " WHERE cfd.fk_commande = ".((int) $this->id);
2431  $sql .= " AND cfd.fk_product = p.rowid";
2432  if ($status >= 0) {
2433  $sql .= " AND cfd.status = ".((int) $status);
2434  }
2435  $sql .= " ORDER BY cfd.rowid ASC";
2436 
2437  $resql = $this->db->query($sql);
2438  if ($resql) {
2439  $num = $this->db->num_rows($resql);
2440  $i = 0;
2441 
2442  while ($i < $num) {
2443  $objp = $this->db->fetch_object($resql);
2444  if ($objp) {
2445  $ret[] = array(
2446  'id' => $objp->dispatchedlineid,
2447  'productid' => $objp->fk_product,
2448  'warehouseid' => $objp->warehouse_id,
2449  'qty' => $objp->qty,
2450  'orderlineid' => $objp->fk_commandefourndet
2451  );
2452  }
2453 
2454  $i++;
2455  }
2456  } else {
2457  dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2458  }
2459 
2460  return $ret;
2461  }
2462 
2463  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2473  public function Livraison($user, $date, $type, $comment)
2474  {
2475  // phpcs:enable
2476  global $conf, $langs;
2477 
2478  $result = 0;
2479  $error = 0;
2480 
2481  dol_syslog(get_class($this)."::Livraison");
2482 
2483  $usercanreceive = 0;
2484  if (!isModEnabled('reception')) {
2485  $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2486  } else {
2487  $usercanreceive = $user->hasRight("reception", "creer");
2488  }
2489 
2490  if ($usercanreceive) {
2491  // Define the new status
2492  if ($type == 'par') {
2494  } elseif ($type == 'tot') {
2496  } elseif ($type == 'nev') {
2498  } elseif ($type == 'can') {
2500  } else {
2501  $error++;
2502  dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2503  return -2;
2504  }
2505 
2506  // Some checks to accept the record
2507  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
2508  // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2509  if (!$error && ($type == 'tot')) {
2510  $dispatchedlinearray = $this->getDispachedLines(0);
2511  if (count($dispatchedlinearray) > 0) {
2512  $result = -1;
2513  $error++;
2514  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2515  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2516  }
2517  }
2518  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)
2519  $dispatcheddenied = $this->getDispachedLines(2);
2520  if (count($dispatchedlinearray) > 0) {
2521  $result = -1;
2522  $error++;
2523  $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2524  dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2525  }
2526  }
2527  }
2528 
2529  // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2530 
2531  if (empty($error)) {
2532  $this->db->begin();
2533 
2534  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2535  $sql .= " SET fk_statut = ".((int) $statut);
2536  $sql .= " WHERE rowid = ".((int) $this->id);
2537  $sql .= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")"; // Process running or Partially received
2538 
2539  dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2540  $resql = $this->db->query($sql);
2541  if ($resql) {
2542  $result = 1;
2543  $old_statut = $this->statut;
2544  $this->statut = $statut;
2545  $this->context['actionmsg2'] = $comment;
2546 
2547  // Call trigger
2548  $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2549  if ($result_trigger < 0) {
2550  $error++;
2551  }
2552  // End call triggers
2553 
2554  if (empty($error)) {
2555  $this->db->commit();
2556  } else {
2557  $this->statut = $old_statut;
2558  $this->db->rollback();
2559  $this->error = $this->db->lasterror();
2560  $result = -1;
2561  }
2562  } else {
2563  $this->db->rollback();
2564  $this->error = $this->db->lasterror();
2565  $result = -1;
2566  }
2567  }
2568  } else {
2569  $this->error = $langs->trans('NotAuthorized');
2570  $this->errors[] = $langs->trans('NotAuthorized');
2571  dol_syslog(get_class($this)."::Livraison Not Authorized");
2572  $result = -3;
2573  }
2574  return $result;
2575  }
2576 
2577  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2587  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2588  {
2589  // phpcs:enable
2590  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2591  }
2592 
2601  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2602  {
2603  if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2604  $error = 0;
2605 
2606  $this->db->begin();
2607 
2608  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2609  $sql .= " SET date_livraison = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
2610  $sql .= " WHERE rowid = ".((int) $this->id);
2611 
2612  dol_syslog(__METHOD__, LOG_DEBUG);
2613  $resql = $this->db->query($sql);
2614  if (!$resql) {
2615  $this->errors[] = $this->db->error();
2616  $error++;
2617  }
2618 
2619  if (!$error) {
2620  $this->oldcopy = clone $this;
2621  $this->date_livraison = $delivery_date;
2622  $this->delivery_date = $delivery_date;
2623  }
2624 
2625  if (!$notrigger && empty($error)) {
2626  // Call trigger
2627  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2628  if ($result < 0) {
2629  $error++;
2630  }
2631  // End call triggers
2632  }
2633 
2634  if (!$error) {
2635  $this->db->commit();
2636  return 1;
2637  } else {
2638  foreach ($this->errors as $errmsg) {
2639  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2640  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2641  }
2642  $this->db->rollback();
2643  return -1 * $error;
2644  }
2645  } else {
2646  return -2;
2647  }
2648  }
2649 
2650  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2659  public function set_id_projet($user, $id_projet, $notrigger = 0)
2660  {
2661  // phpcs:enable
2662  if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2663  $error = 0;
2664 
2665  $this->db->begin();
2666 
2667  $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2668  $sql .= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2669  $sql .= " WHERE rowid = ".((int) $this->id);
2670 
2671  dol_syslog(__METHOD__, LOG_DEBUG);
2672  $resql = $this->db->query($sql);
2673  if (!$resql) {
2674  $this->errors[] = $this->db->error();
2675  $error++;
2676  }
2677 
2678  if (!$error) {
2679  $this->oldcopy = clone $this;
2680  $this->fk_projet = $id_projet;
2681  $this->fk_project = $id_projet;
2682  }
2683 
2684  if (!$notrigger && empty($error)) {
2685  // Call trigger
2686  $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2687  if ($result < 0) {
2688  $error++;
2689  }
2690  // End call triggers
2691  }
2692 
2693  if (!$error) {
2694  $this->db->commit();
2695  return 1;
2696  } else {
2697  foreach ($this->errors as $errmsg) {
2698  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2699  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2700  }
2701  $this->db->rollback();
2702  return -1 * $error;
2703  }
2704  } else {
2705  return -2;
2706  }
2707  }
2708 
2717  public function updateFromCommandeClient($user, $idc, $comclientid)
2718  {
2719  $comclient = new Commande($this->db);
2720  $comclient->fetch($comclientid);
2721 
2722  $this->id = $idc;
2723 
2724  $this->lines = array();
2725 
2726  $num = count($comclient->lines);
2727  for ($i = 0; $i < $num; $i++) {
2728  $prod = new Product($this->db);
2729  $label = '';
2730  $ref = '';
2731  if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2732  $label = $prod->label;
2733  $ref = $prod->ref;
2734  }
2735 
2736  $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2737  $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2738  $sql .= " VALUES (".((int) $idc).", '".$this->db->escape($label)."', '".$this->db->escape($comclient->lines[$i]->desc)."'";
2739  $sql .= ",".$comclient->lines[$i]->fk_product.", ".price2num($comclient->lines[$i]->price, 'MU');
2740  $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);
2741  $sql .= ", '".price2num($comclient->lines[$i]->subprice, 'MT')."','0', '".$this->db->escape($ref)."');";
2742  if ($this->db->query($sql)) {
2743  $this->update_price(1);
2744  }
2745  }
2746 
2747  return 1;
2748  }
2749 
2757  public function setStatus($user, $status)
2758  {
2759  global $conf, $langs;
2760  $error = 0;
2761 
2762  $this->db->begin();
2763 
2764  $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2765  $sql .= " SET fk_statut = ".$status;
2766  $sql .= " WHERE rowid = ".((int) $this->id);
2767 
2768  dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2769  $resql = $this->db->query($sql);
2770  if ($resql) {
2771  // Trigger names for each status
2772  $triggerName = array();
2773  $triggerName[0] = 'DRAFT';
2774  $triggerName[1] = 'VALIDATED';
2775  $triggerName[2] = 'APPROVED';
2776  $triggerName[3] = 'ORDERED'; // Ordered
2777  $triggerName[4] = 'RECEIVED_PARTIALLY';
2778  $triggerName[5] = 'RECEIVED_COMPLETELY';
2779  $triggerName[6] = 'CANCELED';
2780  $triggerName[7] = 'CANCELED';
2781  $triggerName[9] = 'REFUSED';
2782 
2783  // Call trigger
2784  $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_".$triggerName[$status], $user);
2785  if ($result < 0) {
2786  $error++;
2787  }
2788  // End call triggers
2789  } else {
2790  $error++;
2791  $this->error = $this->db->lasterror();
2792  dol_syslog(get_class($this)."::setStatus ".$this->error);
2793  }
2794 
2795  if (!$error) {
2796  $this->statut = $status;
2797  $this->db->commit();
2798  return 1;
2799  } else {
2800  $this->db->rollback();
2801  return -1;
2802  }
2803  }
2804 
2828  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 = '')
2829  {
2830  global $mysoc, $conf, $langs;
2831  dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2832  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2833 
2834  $error = 0;
2835 
2836  if ($this->brouillon) {
2837  // Clean parameters
2838  if (empty($qty)) {
2839  $qty = 0;
2840  }
2841  if (empty($info_bits)) {
2842  $info_bits = 0;
2843  }
2844  if (empty($txtva)) {
2845  $txtva = 0;
2846  }
2847  if (empty($txlocaltax1)) {
2848  $txlocaltax1 = 0;
2849  }
2850  if (empty($txlocaltax2)) {
2851  $txlocaltax2 = 0;
2852  }
2853  if (empty($remise)) {
2854  $remise = 0;
2855  }
2856  if (empty($remise_percent)) {
2857  $remise_percent = 0;
2858  }
2859 
2860  $remise_percent = price2num($remise_percent);
2861  $qty = price2num($qty);
2862  if (!$qty) {
2863  $qty = 1;
2864  }
2865  $pu = price2num($pu);
2866  $pu_ht_devise = price2num($pu_ht_devise);
2867  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
2868  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2869  }
2870  $txlocaltax1 = price2num($txlocaltax1);
2871  $txlocaltax2 = price2num($txlocaltax2);
2872 
2873  // Check parameters
2874  if ($type < 0) {
2875  return -1;
2876  }
2877  if ($date_start && $date_end && $date_start > $date_end) {
2878  $langs->load("errors");
2879  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2880  return -1;
2881  }
2882 
2883  $this->db->begin();
2884 
2885  // Calcul du total TTC et de la TVA pour la ligne a partir de
2886  // qty, pu, remise_percent et txtva
2887  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2888  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2889 
2890  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2891 
2892  // Clean vat code
2893  $reg = array();
2894  $vat_src_code = '';
2895  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2896  $vat_src_code = $reg[1];
2897  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2898  }
2899 
2900  $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);
2901  $total_ht = $tabprice[0];
2902  $total_tva = $tabprice[1];
2903  $total_ttc = $tabprice[2];
2904  $total_localtax1 = $tabprice[9];
2905  $total_localtax2 = $tabprice[10];
2906  $pu_ht = $tabprice[3];
2907  $pu_tva = $tabprice[4];
2908  $pu_ttc = $tabprice[5];
2909 
2910  // MultiCurrency
2911  $multicurrency_total_ht = $tabprice[16];
2912  $multicurrency_total_tva = $tabprice[17];
2913  $multicurrency_total_ttc = $tabprice[18];
2914  $pu_ht_devise = $tabprice[19];
2915 
2916  $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2917  $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2918 
2919  //Fetch current line from the database and then clone the object and set it in $oldline property
2920  $this->line = new CommandeFournisseurLigne($this->db);
2921  $this->line->fetch($rowid);
2922 
2923  $oldline = clone $this->line;
2924  $this->line->oldline = $oldline;
2925 
2926  $this->line->context = $this->context;
2927 
2928  $this->line->fk_commande = $this->id;
2929  //$this->line->label=$label;
2930  $this->line->desc = $desc;
2931 
2932  // redefine quantity according to packaging
2933  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
2934  if ($qty < $this->line->packaging) {
2935  $qty = $this->line->packaging;
2936  } else {
2937  if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
2938  $coeff = intval($qty / $this->line->packaging) + 1;
2939  $qty = $this->line->packaging * $coeff;
2940  setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
2941  }
2942  }
2943  }
2944 
2945  $this->line->qty = $qty;
2946  $this->line->ref_supplier = $ref_supplier;
2947 
2948  $this->line->vat_src_code = $vat_src_code;
2949  $this->line->tva_tx = $txtva;
2950  $this->line->localtax1_tx = $txlocaltax1;
2951  $this->line->localtax2_tx = $txlocaltax2;
2952  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2953  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2954  $this->line->remise_percent = $remise_percent;
2955  $this->line->subprice = $pu_ht;
2956  $this->line->rang = $this->rang;
2957  $this->line->info_bits = $info_bits;
2958  $this->line->total_ht = $total_ht;
2959  $this->line->total_tva = $total_tva;
2960  $this->line->total_localtax1 = $total_localtax1;
2961  $this->line->total_localtax2 = $total_localtax2;
2962  $this->line->total_ttc = $total_ttc;
2963  $this->line->product_type = $type;
2964  $this->line->special_code = (!empty($this->special_code) ? $this->special_code : 0);
2965  $this->line->origin = $this->origin;
2966  $this->line->fk_unit = $fk_unit;
2967 
2968  $this->line->date_start = $date_start;
2969  $this->line->date_end = $date_end;
2970 
2971  // Multicurrency
2972  $this->line->fk_multicurrency = $this->fk_multicurrency;
2973  $this->line->multicurrency_code = $this->multicurrency_code;
2974  $this->line->multicurrency_subprice = $pu_ht_devise;
2975  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2976  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2977  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2978 
2979  $this->line->subprice = $pu_ht;
2980  $this->line->price = $this->line->subprice;
2981 
2982  $this->line->remise_percent = $remise_percent;
2983 
2984  if (is_array($array_options) && count($array_options) > 0) {
2985  // We replace values in this->line->array_options only for entries defined into $array_options
2986  foreach ($array_options as $key => $value) {
2987  $this->line->array_options[$key] = $array_options[$key];
2988  }
2989  }
2990 
2991  $result = $this->line->update($notrigger);
2992 
2993 
2994  // Mise a jour info denormalisees au niveau facture
2995  if ($result >= 0) {
2996  $this->update_price('1', 'auto');
2997  $this->db->commit();
2998  return $result;
2999  } else {
3000  $this->error = $this->db->lasterror();
3001  $this->db->rollback();
3002  return -1;
3003  }
3004  } else {
3005  $this->error = "Order status makes operation forbidden";
3006  dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
3007  return -2;
3008  }
3009  }
3010 
3011 
3019  public function initAsSpecimen()
3020  {
3021  global $user, $langs, $conf;
3022 
3023  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
3024 
3025  dol_syslog(get_class($this)."::initAsSpecimen");
3026 
3027  $now = dol_now();
3028 
3029  // Find first product
3030  $prodid = 0;
3031  $product = new ProductFournisseur($this->db);
3032  $sql = "SELECT rowid";
3033  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3034  $sql .= " WHERE entity IN (".getEntity('product').")";
3035  $sql .= $this->db->order("rowid", "ASC");
3036  $sql .= $this->db->plimit(1);
3037  $resql = $this->db->query($sql);
3038  if ($resql) {
3039  $obj = $this->db->fetch_object($resql);
3040  $prodid = $obj->rowid;
3041  }
3042 
3043  // Initialise parametres
3044  $this->id = 0;
3045  $this->ref = 'SPECIMEN';
3046  $this->specimen = 1;
3047  $this->socid = 1;
3048  $this->date = $now;
3049  $this->date_commande = $now;
3050  $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3051  $this->cond_reglement_code = 'RECEP';
3052  $this->mode_reglement_code = 'CHQ';
3053 
3054  $this->note_public = 'This is a comment (public)';
3055  $this->note_private = 'This is a comment (private)';
3056 
3057  $this->multicurrency_tx = 1;
3058  $this->multicurrency_code = $conf->currency;
3059 
3060  $this->statut = 0;
3061 
3062  // Lines
3063  $nbp = 5;
3064  $xnbp = 0;
3065  while ($xnbp < $nbp) {
3066  $line = new CommandeFournisseurLigne($this->db);
3067  $line->desc = $langs->trans("Description")." ".$xnbp;
3068  $line->qty = 1;
3069  $line->subprice = 100;
3070  $line->price = 100;
3071  $line->tva_tx = 19.6;
3072  $line->localtax1_tx = 0;
3073  $line->localtax2_tx = 0;
3074  if ($xnbp == 2) {
3075  $line->total_ht = 50;
3076  $line->total_ttc = 59.8;
3077  $line->total_tva = 9.8;
3078  $line->remise_percent = 50;
3079  } else {
3080  $line->total_ht = 100;
3081  $line->total_ttc = 119.6;
3082  $line->total_tva = 19.6;
3083  $line->remise_percent = 00;
3084  }
3085  $line->fk_product = $prodid;
3086 
3087  $this->lines[$xnbp] = $line;
3088 
3089  $this->total_ht += $line->total_ht;
3090  $this->total_tva += $line->total_tva;
3091  $this->total_ttc += $line->total_ttc;
3092 
3093  $xnbp++;
3094  }
3095  }
3096 
3103  public function info($id)
3104  {
3105  $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3106  $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3107  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
3108  $sql .= ' WHERE c.rowid = '.((int) $id);
3109 
3110  $result = $this->db->query($sql);
3111  if ($result) {
3112  if ($this->db->num_rows($result)) {
3113  $obj = $this->db->fetch_object($result);
3114  $this->id = $obj->rowid;
3115  if ($obj->fk_user_author) {
3116  $this->user_creation_id = $obj->fk_user_author;
3117  }
3118  if ($obj->fk_user_valid) {
3119  $this->user_validation_id = $obj->fk_user_valid;
3120  }
3121  if ($obj->fk_user_modif) {
3122  $this->user_modification_id = $obj->fk_user_modif;
3123  }
3124  if ($obj->fk_user_approve) {
3125  $this->user_approve_id = $obj->fk_user_approve;
3126  }
3127  if ($obj->fk_user_approve2) {
3128  $this->user_approve_id2 = $obj->fk_user_approve2;
3129  }
3130 
3131  $this->date_creation = $this->db->jdate($obj->datec);
3132  $this->date_modification = $this->db->jdate($obj->datem);
3133  $this->date_approve = $this->db->jdate($obj->datea);
3134  $this->date_approve2 = $this->db->jdate($obj->datea2);
3135  $this->date_validation = $this->db->jdate($obj->date_validation);
3136  }
3137  $this->db->free($result);
3138  } else {
3139  dol_print_error($this->db);
3140  }
3141  }
3142 
3143  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3149  public function load_state_board()
3150  {
3151  // phpcs:enable
3152  global $conf, $user;
3153 
3154  $this->nb = array();
3155  $clause = "WHERE";
3156 
3157  $sql = "SELECT count(co.rowid) as nb";
3158  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
3159  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3160  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3161  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3162  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3163  $clause = "AND";
3164  }
3165  $sql .= " ".$clause." co.entity IN (".getEntity('supplier_order').")";
3166 
3167  $resql = $this->db->query($sql);
3168  if ($resql) {
3169  while ($obj = $this->db->fetch_object($resql)) {
3170  $this->nb["supplier_orders"] = $obj->nb;
3171  }
3172  $this->db->free($resql);
3173  return 1;
3174  } else {
3175  dol_print_error($this->db);
3176  $this->error = $this->db->error();
3177  return -1;
3178  }
3179  }
3180 
3181  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3189  public function load_board($user, $mode = 'opened')
3190  {
3191  // phpcs:enable
3192  global $conf, $langs;
3193 
3194  $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3195  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
3196  if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3197  $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
3198  }
3199  $sql .= " WHERE c.entity = ".$conf->entity;
3200  if ($mode === 'awaiting') {
3201  $sql .= " AND c.fk_statut IN (".self::STATUS_ORDERSENT.", ".self::STATUS_RECEIVED_PARTIALLY.")";
3202  } else {
3203  $sql .= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
3204  }
3205  if ($user->socid) {
3206  $sql .= " AND c.fk_soc = ".((int) $user->socid);
3207  }
3208 
3209  $resql = $this->db->query($sql);
3210  if ($resql) {
3211  $commandestatic = new CommandeFournisseur($this->db);
3212 
3213  $response = new WorkboardResponse();
3214  $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3215  $response->label = $langs->trans("SuppliersOrdersToProcess");
3216  $response->labelShort = $langs->trans("Opened");
3217  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3218  $response->img = img_object('', "order");
3219 
3220  if ($mode === 'awaiting') {
3221  $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3222  $response->labelShort = $langs->trans("AwaitingReception");
3223  $response->url = DOL_URL_ROOT.'/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3224  }
3225 
3226  while ($obj = $this->db->fetch_object($resql)) {
3227  $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3228  $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3229  $commandestatic->statut = $obj->fk_statut;
3230 
3231  $response->nbtodo++;
3232  $response->total += $obj->total_ht;
3233 
3234  if ($commandestatic->hasDelay()) {
3235  $response->nbtodolate++;
3236  }
3237  }
3238 
3239  return $response;
3240  } else {
3241  $this->error = $this->db->error();
3242  return -1;
3243  }
3244  }
3245 
3252  public function getInputMethod()
3253  {
3254  global $db, $langs;
3255 
3256  if ($this->methode_commande_id > 0) {
3257  $sql = "SELECT rowid, code, libelle as label";
3258  $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_method';
3259  $sql .= " WHERE active=1 AND rowid = ".((int) $this->methode_commande_id);
3260 
3261  $resql = $this->db->query($sql);
3262  if ($resql) {
3263  if ($this->db->num_rows($resql)) {
3264  $obj = $this->db->fetch_object($resql);
3265 
3266  $string = $langs->trans($obj->code);
3267  if ($string == $obj->code) {
3268  $string = $obj->label != '-' ? $obj->label : '';
3269  }
3270  return $string;
3271  }
3272  } else {
3273  dol_print_error($this->db);
3274  }
3275  }
3276 
3277  return '';
3278  }
3279 
3291  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3292  {
3293  global $conf, $langs;
3294 
3295  if (!dol_strlen($modele)) {
3296  $modele = ''; // No doc template/generation by default
3297 
3298  if (!empty($this->model_pdf)) {
3299  $modele = $this->model_pdf;
3300  } elseif (!empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
3301  $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3302  }
3303  }
3304 
3305  if (empty($modele)) {
3306  return 0;
3307  } else {
3308  $langs->load("suppliers");
3309  $outputlangs->load("products");
3310 
3311  $modelpath = "core/modules/supplier_order/doc/";
3312  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3313  }
3314  }
3315 
3322  public function getMaxDeliveryTimeDay($langs)
3323  {
3324  if (empty($this->lines)) {
3325  return '';
3326  }
3327 
3328  $obj = new ProductFournisseur($this->db);
3329 
3330  $nb = 0;
3331  foreach ($this->lines as $line) {
3332  if ($line->fk_product > 0) {
3333  $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3334  if ($idp) {
3335  $obj->fetch($idp);
3336  if ($obj->delivery_time_days > $nb) {
3337  $nb = $obj->delivery_time_days;
3338  }
3339  }
3340  }
3341  }
3342 
3343  if ($nb === 0) {
3344  return '';
3345  } else {
3346  return $nb.' '.$langs->trans('Days');
3347  }
3348  }
3349 
3354  public function getRights()
3355  {
3356  global $user;
3357 
3358  return $user->hasRight("fournisseur", "commande");
3359  }
3360 
3361 
3370  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3371  {
3372  $tables = array(
3373  'commande_fournisseur'
3374  );
3375 
3376  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3377  }
3378 
3387  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3388  {
3389  $tables = array(
3390  'commande_fournisseurdet'
3391  );
3392 
3393  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3394  }
3395 
3403  public function hasDelay()
3404  {
3405  global $conf;
3406 
3407  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3408  $this->delivery_date = $this->date_livraison; // For backward compatibility
3409  }
3410 
3411  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3412  $now = dol_now();
3413  if (!empty($this->delivery_date)) {
3414  $date_to_test = $this->delivery_date;
3415  return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3416  } else {
3417  //$date_to_test = $this->date_commande;
3418  //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3419  return false;
3420  }
3421  } else {
3422  $now = dol_now();
3423  $date_to_test = $this->date_commande;
3424 
3425  return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3426  }
3427  }
3428 
3436  public function showDelay()
3437  {
3438  global $conf, $langs;
3439 
3440  if (empty($this->delivery_date) && !empty($this->date_livraison)) {
3441  $this->delivery_date = $this->date_livraison; // For backward compatibility
3442  }
3443 
3444  $text = '';
3445 
3446  if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3447  if (!empty($this->delivery_date)) {
3448  $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->delivery_date, 'day');
3449  } else {
3450  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3451  }
3452  } else {
3453  $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3454  }
3455  if ($text) {
3456  $text .= ' '.($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3457  }
3458 
3459  return $text;
3460  }
3461 
3462 
3471  public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3472  {
3473  global $conf, $langs;
3474 
3475  if (isModEnabled("supplier_order")) {
3476  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3477 
3478  $qtydelivered = array();
3479  $qtywished = array();
3480 
3481  $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3482  $filter = array('t.fk_commande'=>$this->id);
3483  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3484  $filter['t.status'] = 1; // Restrict to lines with status validated
3485  }
3486 
3487  $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3488  if ($ret < 0) {
3489  $this->error = $supplierorderdispatch->error; $this->errors = $supplierorderdispatch->errors;
3490  return $ret;
3491  } else {
3492  if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3493  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3494  $date_liv = dol_now();
3495 
3496  // Build array with quantity deliverd by product
3497  foreach ($supplierorderdispatch->lines as $line) {
3498  $qtydelivered[$line->fk_product] += $line->qty;
3499  }
3500  foreach ($this->lines as $line) {
3501  // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3502  if (empty($conf->global->STOCK_SUPPORTS_SERVICES) && $line->product_type > 0) {
3503  continue;
3504  }
3505  $qtywished[$line->fk_product] += $line->qty;
3506  }
3507 
3508  //Compare array
3509  $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3510  $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3511  $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3512  //var_dump(array_keys($qtydelivered));
3513  //var_dump(array_keys($qtywished));
3514  //var_dump($diff_array);
3515  //var_dump($keysinwishednotindelivered);
3516  //var_dump($keysindeliverednotinwished);
3517  //exit;
3518 
3519  if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everythings is received
3520  if ($closeopenorder) {
3521  //$ret=$this->setStatus($user,5);
3522  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3523  if ($ret < 0) {
3524  return -1;
3525  }
3526  return 5;
3527  } else {
3528  //Diff => received partially
3529  //$ret=$this->setStatus($user,4);
3530  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3531  if ($ret < 0) {
3532  return -1;
3533  }
3534  return 4;
3535  }
3536  } elseif (!empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED)) {
3537  //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3538 
3539  $close = 0;
3540 
3541  if (count($diff_array) > 0) {
3542  //there are some difference between the two arrays
3543 
3544  //scan the array of results
3545  foreach ($diff_array as $key => $value) {
3546  //if the quantity delivered is greater or equal to wish quantity
3547  if ($qtydelivered[$key] >= $qtywished[$key]) {
3548  $close++;
3549  }
3550  }
3551  }
3552 
3553 
3554  if ($close == count($diff_array)) {
3555  //all the products are received equal or more than the wished quantity
3556  if ($closeopenorder) {
3557  $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3558  if ($ret < 0) {
3559  return -1;
3560  }
3561  return 5;
3562  } else {
3563  //Diff => received partially
3564  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3565  if ($ret < 0) {
3566  return -1;
3567  }
3568  return 4;
3569  }
3570  } else {
3571  //all the products are not received
3572  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3573  if ($ret < 0) {
3574  return -1;
3575  }
3576  return 4;
3577  }
3578  } else {
3579  //Diff => received partially
3580  $ret = $this->Livraison($user, $date_liv, 'par', $comment); // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3581  if ($ret < 0) {
3582  return -1;
3583  }
3584  return 4;
3585  }
3586  }
3587  return 1;
3588  }
3589  }
3590  return 0;
3591  }
3592 
3600  public function loadReceptions($filtre_statut = -1)
3601  {
3602  $this->receptions = array();
3603 
3604  dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3605 
3606  $sql = 'SELECT cd.rowid, cd.fk_product,';
3607  $sql .= ' sum(cfd.qty) as qty';
3608  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3609  if ($filtre_statut >= 0) {
3610  $sql .= ' '.MAIN_DB_PREFIX.'reception as e,';
3611  }
3612  $sql .= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3613  $sql .= ' WHERE';
3614  if ($filtre_statut >= 0) {
3615  $sql .= ' cfd.fk_reception = e.rowid AND';
3616  }
3617  $sql .= ' cfd.fk_commandefourndet = cd.rowid';
3618  $sql .= ' AND cd.fk_commande ='.((int) $this->id);
3619  if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3620  $sql .= ' AND cd.fk_product = '.((int) $this->fk_product);
3621  }
3622  if ($filtre_statut >= 0) {
3623  $sql .= ' AND e.fk_statut >= '.((int) $filtre_statut);
3624  }
3625  $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3626 
3627  $resql = $this->db->query($sql);
3628  if ($resql) {
3629  $num = $this->db->num_rows($resql);
3630  $i = 0;
3631  while ($i < $num) {
3632  $obj = $this->db->fetch_object($resql);
3633  empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3634  $i++;
3635  }
3636  $this->db->free($resql);
3637 
3638  return $num;
3639  } else {
3640  $this->error = $this->db->lasterror();
3641  return -1;
3642  }
3643  }
3644 
3652  public function getKanbanView($option = '', $arraydata = null)
3653  {
3654  global $langs;
3655 
3656  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3657 
3658  $return = '<div class="box-flex-item box-flex-grow-zero">';
3659  $return .= '<div class="info-box info-box-sm">';
3660  $return .= '<span class="info-box-icon bg-infobox-action">';
3661  $return .= img_picto('', $this->picto);
3662  $return .= '</span>';
3663  $return .= '<div class="info-box-content">';
3664  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3665  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3666  if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3667  $return .='<br><span class="info-box-label amount">'.$this->socid.'</span>';
3668  }
3669  if (property_exists($this, 'billed')) {
3670  $return .= '<br><span class="opacitymedium">'.$langs->trans("Billed").' : </span><span class="info-box-label">'.yn($this->billed).'</span>';
3671  }
3672  if (method_exists($this, 'getLibStatut')) {
3673  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3674  }
3675  $return .= '</div>';
3676  $return .= '</div>';
3677  $return .= '</div>';
3678  return $return;
3679  }
3680 }
3681 
3682 
3683 
3688 {
3692  public $element = 'commande_fournisseurdet';
3693 
3697  public $table_element = 'commande_fournisseurdet';
3698 
3699  public $oldline;
3700 
3705  public $fk_commande;
3706 
3707  // From llx_commande_fournisseurdet
3711  public $fk_parent_line;
3712 
3716  public $fk_facture;
3717 
3718  public $rang = 0;
3719  public $special_code = 0;
3720 
3725  public $pu_ht;
3726 
3727  public $date_start;
3728  public $date_end;
3729 
3730  // From llx_product_fournisseur_price
3731 
3736  public $ref_supplier;
3737 
3743  public $ref_fourn;
3744 
3745  public $remise;
3746 
3747 
3753  public function __construct($db)
3754  {
3755  $this->db = $db;
3756  }
3757 
3764  public function fetch($rowid)
3765  {
3766  global $conf;
3767 
3768  $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3769  $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,';
3770  $sql .= ' cd.remise, cd.remise_percent, cd.subprice,';
3771  $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3772  $sql .= ' cd.total_localtax1, cd.total_localtax2,';
3773  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3774  $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,';
3775  $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3776  $sql .= ' c.fk_soc as socid';
3777  $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3778  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3779  $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid);
3780 
3781  $result = $this->db->query($sql);
3782  if ($result) {
3783  $objp = $this->db->fetch_object($result);
3784 
3785  if (!empty($objp)) {
3786  $this->rowid = $objp->rowid;
3787  $this->id = $objp->rowid;
3788  $this->fk_commande = $objp->fk_commande;
3789  $this->desc = $objp->description;
3790  $this->qty = $objp->qty;
3791  $this->ref_fourn = $objp->ref_supplier;
3792  $this->ref_supplier = $objp->ref_supplier;
3793  $this->subprice = $objp->subprice;
3794  $this->tva_tx = $objp->tva_tx;
3795  $this->localtax1_tx = $objp->localtax1_tx;
3796  $this->localtax2_tx = $objp->localtax2_tx;
3797  $this->localtax1_type = $objp->localtax1_type;
3798  $this->localtax2_type = $objp->localtax2_type;
3799  $this->remise = $objp->remise;
3800  $this->remise_percent = $objp->remise_percent;
3801  $this->fk_product = $objp->fk_product;
3802  $this->info_bits = $objp->info_bits;
3803  $this->total_ht = $objp->total_ht;
3804  $this->total_tva = $objp->total_tva;
3805  $this->total_localtax1 = $objp->total_localtax1;
3806  $this->total_localtax2 = $objp->total_localtax2;
3807  $this->total_ttc = $objp->total_ttc;
3808  $this->product_type = $objp->product_type;
3809  $this->special_code = $objp->special_code;
3810 
3811  $this->ref = $objp->product_ref;
3812 
3813  $this->product_ref = $objp->product_ref;
3814  $this->product_label = $objp->product_label;
3815  $this->product_desc = $objp->product_desc;
3816 
3817  if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) {
3818  // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
3819  // Move this into another method and call it when required.
3820 
3821  // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
3822  $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price";
3823  $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")";
3824  $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product);
3825  $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'";
3826  $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified
3827  $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified
3828  $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid);
3829  $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first
3830  $sqlsearchpackage .= " LIMIT 1";
3831 
3832  $resqlsearchpackage = $this->db->query($sqlsearchpackage);
3833  if ($resqlsearchpackage) {
3834  $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
3835  if ($objsearchpackage) {
3836  $this->fk_fournprice = $objsearchpackage->rowid;
3837  $this->packaging = $objsearchpackage->packaging;
3838  }
3839  } else {
3840  $this->error = $this->db->lasterror();
3841  return -1;
3842  }
3843  }
3844 
3845  $this->date_start = $this->db->jdate($objp->date_start);
3846  $this->date_end = $this->db->jdate($objp->date_end);
3847  $this->fk_unit = $objp->fk_unit;
3848 
3849  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3850  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3851  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3852  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3853 
3854  $this->fetch_optionals();
3855 
3856  $this->db->free($result);
3857  return 1;
3858  } else {
3859  $this->error = 'Supplier order line with id='.$rowid.' not found';
3860  dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3861  return 0;
3862  }
3863  } else {
3864  dol_print_error($this->db);
3865  return -1;
3866  }
3867  }
3868 
3875  public function insert($notrigger = 0)
3876  {
3877  global $conf, $user;
3878 
3879  $error = 0;
3880 
3881  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3882 
3883  // Clean parameters
3884  if (empty($this->tva_tx)) {
3885  $this->tva_tx = 0;
3886  }
3887  if (empty($this->localtax1_tx)) {
3888  $this->localtax1_tx = 0;
3889  }
3890  if (empty($this->localtax2_tx)) {
3891  $this->localtax2_tx = 0;
3892  }
3893  if (empty($this->localtax1_type)) {
3894  $this->localtax1_type = '0';
3895  }
3896  if (empty($this->localtax2_type)) {
3897  $this->localtax2_type = '0';
3898  }
3899  if (empty($this->total_localtax1)) {
3900  $this->total_localtax1 = 0;
3901  }
3902  if (empty($this->total_localtax2)) {
3903  $this->total_localtax2 = 0;
3904  }
3905  if (empty($this->rang)) {
3906  $this->rang = 0;
3907  }
3908  if (empty($this->remise_percent)) {
3909  $this->remise_percent = 0;
3910  }
3911  if (empty($this->info_bits)) {
3912  $this->info_bits = 0;
3913  }
3914  if (empty($this->special_code)) {
3915  $this->special_code = 0;
3916  }
3917  if (empty($this->fk_parent_line)) {
3918  $this->fk_parent_line = 0;
3919  }
3920  if (empty($this->pa_ht)) {
3921  $this->pa_ht = 0;
3922  }
3923 
3924  // Multicurrency
3925  if (!empty($this->multicurrency_code)) {
3926  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3927  }
3928  if (empty($this->fk_multicurrency)) {
3929  $this->multicurrency_code = $conf->currency;
3930  $this->fk_multicurrency = 0;
3931  $this->multicurrency_tx = 1;
3932  }
3933 
3934  // Check parameters
3935  if ($this->product_type < 0) {
3936  return -1;
3937  }
3938 
3939  $this->db->begin();
3940 
3941  // Insertion dans base de la ligne
3942  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3943  $sql .= " (fk_commande, label, description, date_start, date_end,";
3944  $sql .= " fk_product, product_type, special_code, rang,";
3945  $sql .= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3946  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3947  $sql .= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3948  $sql .= ")";
3949  $sql .= " VALUES (".$this->fk_commande.", '".$this->db->escape($this->label)."','".$this->db->escape($this->desc)."',";
3950  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3951  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3952  if ($this->fk_product) {
3953  $sql .= $this->fk_product.",";
3954  } else {
3955  $sql .= "null,";
3956  }
3957  $sql .= "'".$this->db->escape($this->product_type)."',";
3958  $sql .= "'".$this->db->escape($this->special_code)."',";
3959  $sql .= "'".$this->db->escape($this->rang)."',";
3960  $sql .= "'".$this->db->escape($this->qty)."', ";
3961  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3962  $sql .= " ".price2num($this->tva_tx).", ";
3963  $sql .= " ".price2num($this->localtax1_tx).",";
3964  $sql .= " ".price2num($this->localtax2_tx).",";
3965  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3966  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3967  $sql .= " ".((float) $this->remise_percent).", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3968  $sql .= " ".price2num($this->total_ht).",";
3969  $sql .= " ".price2num($this->total_tva).",";
3970  $sql .= " ".price2num($this->total_localtax1).",";
3971  $sql .= " ".price2num($this->total_localtax2).",";
3972  $sql .= " ".price2num($this->total_ttc).",";
3973  $sql .= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
3974  $sql .= ", ".($this->fk_multicurrency ? ((int) $this->fk_multicurrency) : "null");
3975  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3976  $sql .= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3977  $sql .= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3978  $sql .= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3979  $sql .= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3980  $sql .= ")";
3981 
3982  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3983  $resql = $this->db->query($sql);
3984  if ($resql) {
3985  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3986  $this->rowid = $this->id;
3987 
3988  if (!$error) {
3989  $result = $this->insertExtraFields();
3990  if ($result < 0) {
3991  $error++;
3992  }
3993  }
3994 
3995  if (!$error && !$notrigger) {
3996  // Call trigger
3997  $result = $this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3998  if ($result < 0) {
3999  $error++;
4000  }
4001  // End call triggers
4002  }
4003 
4004  if (!$error) {
4005  $this->db->commit();
4006  return 1;
4007  }
4008 
4009  foreach ($this->errors as $errmsg) {
4010  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4011  $this->errors[] = ($this->errors ? ', '.$errmsg : $errmsg);
4012  }
4013  $this->db->rollback();
4014  return -1 * $error;
4015  } else {
4016  $this->errors[] = $this->db->error();
4017  $this->db->rollback();
4018  return -2;
4019  }
4020  }
4027  public function update($notrigger = 0)
4028  {
4029  global $conf, $user;
4030 
4031  $error = 0;
4032 
4033  $this->db->begin();
4034 
4035  // Mise a jour ligne en base
4036  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
4037  $sql .= " description='".$this->db->escape($this->desc)."'";
4038  $sql .= ", ref='".$this->db->escape($this->ref_supplier)."'";
4039  $sql .= ", subprice='".price2num($this->subprice)."'";
4040  //$sql.= ",remise='".price2num($remise)."'";
4041  $sql .= ", remise_percent='".price2num($this->remise_percent)."'";
4042 
4043  $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4044  $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4045  $sql .= ", localtax1_tx='".price2num($this->localtax1_tx)."'";
4046  $sql .= ", localtax2_tx='".price2num($this->localtax2_tx)."'";
4047  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4048  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4049  $sql .= ", qty='".price2num($this->qty)."'";
4050  $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4051  $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4052  $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4053  $sql .= ", total_ht='".price2num($this->total_ht)."'";
4054  $sql .= ", total_tva='".price2num($this->total_tva)."'";
4055  $sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
4056  $sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
4057  $sql .= ", total_ttc='".price2num($this->total_ttc)."'";
4058  $sql .= ", product_type=".$this->product_type;
4059  $sql .= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
4060  $sql .= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'" : ", fk_unit=null");
4061 
4062  // Multicurrency
4063  $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4064  $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4065  $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4066  $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4067 
4068  $sql .= " WHERE rowid = ".((int) $this->id);
4069 
4070  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
4071  $resql = $this->db->query($sql);
4072  if ($resql) {
4073  if (!$error) {
4074  $result = $this->insertExtraFields();
4075  if ($result < 0) {
4076  $error++;
4077  }
4078  }
4079 
4080  if (!$error && !$notrigger) {
4081  // Call trigger
4082  $result = $this->call_trigger('LINEORDER_SUPPLIER_MODIFY', $user);
4083  if ($result < 0) {
4084  $this->db->rollback();
4085  return -1;
4086  }
4087  // End call triggers
4088  }
4089 
4090  if (!$error) {
4091  $this->db->commit();
4092  return 1;
4093  } else {
4094  $this->db->rollback();
4095  return -1;
4096  }
4097  } else {
4098  $this->error = $this->db->lasterror();
4099  $this->db->rollback();
4100  return -1;
4101  }
4102  }
4103 
4110  public function delete($notrigger = 0)
4111  {
4112  global $user;
4113 
4114  $error = 0;
4115 
4116  $this->db->begin();
4117 
4118  // extrafields
4119  $result = $this->deleteExtraFields();
4120  if ($result < 0) {
4121  $this->db->rollback();
4122  return -1;
4123  }
4124 
4125  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".((int) $this->id);
4126 
4127  dol_syslog(__METHOD__, LOG_DEBUG);
4128  $resql = $this->db->query($sql);
4129  if ($resql) {
4130  if (!$notrigger) {
4131  // Call trigger
4132  $result = $this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
4133  if ($result < 0) {
4134  $error++;
4135  }
4136  // End call triggers
4137  }
4138 
4139  if (!$error) {
4140  $this->db->commit();
4141  return 1;
4142  }
4143 
4144  foreach ($this->errors as $errmsg) {
4145  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4146  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4147  }
4148  $this->db->rollback();
4149  return -1 * $error;
4150  } else {
4151  $this->error = $this->db->lasterror();
4152  return -1;
4153  }
4154  }
4155 }
$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:1485
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:1334
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