dolibarr  9.0.0
expedition.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
4  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
5  * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
6  * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8  * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
9  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
10  * Copyright (C) 2014-2017 Francis Appels <francis.appels@yahoo.com>
11  * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12  * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
13  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
14  * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
28  */
29 
36 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
37 require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
38 if (! empty($conf->propal->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
39 if (! empty($conf->commande->enabled)) require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
40 if (! empty($conf->productbatch->enabled)) require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
41 
42 
46 class Expedition extends CommonObject
47 {
51  public $element="shipping";
52 
56  public $fk_element="fk_expedition";
57 
61  public $table_element="expedition";
62 
66  public $table_element_line="expeditiondet";
67 
72  public $ismultientitymanaged = 1;
73 
77  public $picto = 'sending';
78 
79  public $socid;
80 
84  public $ref_customer;
85 
89  public $ref_int;
90 
91  public $brouillon;
92 
96  public $entrepot_id;
97  public $lines=array();
98 
102  public $tracking_number;
103 
107  public $tracking_url;
108  public $billed;
109 
113  public $model_pdf;
114 
115  public $trueWeight;
116  public $weight_units;
117  public $trueWidth;
118  public $width_units;
119  public $trueHeight;
120  public $height_units;
121  public $trueDepth;
122  public $depth_units;
123  // A denormalized value
124  public $trueSize;
125 
126  public $date_delivery; // Date delivery planed
127 
132  public $date;
133 
139 
144  public $date_shipping;
145 
146  public $date_creation;
147  public $date_valid;
148 
149  public $meths;
150  public $listmeths; // List of carriers
151 
155  const STATUS_DRAFT = 0;
156 
160  const STATUS_VALIDATED = 1;
161 
165  const STATUS_CLOSED = 2;
166 
167 
168 
174  function __construct($db)
175  {
176  global $conf;
177 
178  $this->db = $db;
179  $this->lines = array();
180  $this->products = array();
181 
182  // List of long language codes for status
183  $this->statuts = array();
184  $this->statuts[-1] = 'StatusSendingCanceled';
185  $this->statuts[0] = 'StatusSendingDraft';
186  $this->statuts[1] = 'StatusSendingValidated';
187  $this->statuts[2] = 'StatusSendingProcessed';
188 
189  // List of short language codes for status
190  $this->statutshorts = array();
191  $this->statutshorts[-1] = 'StatusSendingCanceledShort';
192  $this->statutshorts[0] = 'StatusSendingDraftShort';
193  $this->statutshorts[1] = 'StatusSendingValidatedShort';
194  $this->statutshorts[2] = 'StatusSendingProcessedShort';
195 
196  /* Status "billed" or not is managed by another field than status
197  if (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))
198  {
199  $this->statuts[2] = 'StatusSendingBilled';
200  $this->statutshorts[2] = 'StatusSendingBilledShort';
201  }*/
202  }
203 
210  function getNextNumRef($soc)
211  {
212  global $langs, $conf;
213  $langs->load("sendings");
214 
215  if (!empty($conf->global->EXPEDITION_ADDON_NUMBER))
216  {
217  $mybool = false;
218 
219  $file = $conf->global->EXPEDITION_ADDON_NUMBER.".php";
220  $classname = $conf->global->EXPEDITION_ADDON_NUMBER;
221 
222  // Include file with class
223  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
224 
225  foreach ($dirmodels as $reldir) {
226 
227  $dir = dol_buildpath($reldir."core/modules/expedition/");
228 
229  // Load file with numbering class (if found)
230  $mybool|=@include_once $dir.$file;
231  }
232 
233  if (! $mybool)
234  {
235  dol_print_error('',"Failed to include file ".$file);
236  return '';
237  }
238 
239  $obj = new $classname();
240  $numref = "";
241  $numref = $obj->getNextValue($soc,$this);
242 
243  if ( $numref != "")
244  {
245  return $numref;
246  }
247  else
248  {
249  dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
250  return "";
251  }
252  }
253  else
254  {
255  print $langs->trans("Error")." ".$langs->trans("Error_EXPEDITION_ADDON_NUMBER_NotDefined");
256  return "";
257  }
258  }
259 
267  function create($user, $notrigger=0)
268  {
269  global $conf, $hookmanager;
270 
271  $now=dol_now();
272 
273  require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php';
274  $error = 0;
275 
276  // Clean parameters
277  $this->brouillon = 1;
278  $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
279  if (empty($this->fk_project)) $this->fk_project = 0;
280 
281  $this->user = $user;
282 
283 
284  $this->db->begin();
285 
286  $sql = "INSERT INTO ".MAIN_DB_PREFIX."expedition (";
287  $sql.= "ref";
288  $sql.= ", entity";
289  $sql.= ", ref_customer";
290  $sql.= ", ref_int";
291  $sql.= ", date_creation";
292  $sql.= ", fk_user_author";
293  $sql.= ", date_expedition";
294  $sql.= ", date_delivery";
295  $sql.= ", fk_soc";
296  $sql.= ", fk_projet";
297  $sql.= ", fk_address";
298  $sql.= ", fk_shipping_method";
299  $sql.= ", tracking_number";
300  $sql.= ", weight";
301  $sql.= ", size";
302  $sql.= ", width";
303  $sql.= ", height";
304  $sql.= ", weight_units";
305  $sql.= ", size_units";
306  $sql.= ", note_private";
307  $sql.= ", note_public";
308  $sql.= ", model_pdf";
309  $sql.= ", fk_incoterms, location_incoterms";
310  $sql.= ") VALUES (";
311  $sql.= "'(PROV)'";
312  $sql.= ", ".$conf->entity;
313  $sql.= ", ".($this->ref_customer?"'".$this->db->escape($this->ref_customer)."'":"null");
314  $sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
315  $sql.= ", '".$this->db->idate($now)."'";
316  $sql.= ", ".$user->id;
317  $sql.= ", ".($this->date_expedition>0?"'".$this->db->idate($this->date_expedition)."'":"null");
318  $sql.= ", ".($this->date_delivery>0?"'".$this->db->idate($this->date_delivery)."'":"null");
319  $sql.= ", ".$this->socid;
320  $sql.= ", ".$this->fk_project;
321  $sql.= ", ".($this->fk_delivery_address>0?$this->fk_delivery_address:"null");
322  $sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:"null");
323  $sql.= ", '".$this->db->escape($this->tracking_number)."'";
324  $sql.= ", ".$this->weight;
325  $sql.= ", ".$this->sizeS; // TODO Should use this->trueDepth
326  $sql.= ", ".$this->sizeW; // TODO Should use this->trueWidth
327  $sql.= ", ".$this->sizeH; // TODO Should use this->trueHeight
328  $sql.= ", ".$this->weight_units;
329  $sql.= ", ".$this->size_units;
330  $sql.= ", ".(!empty($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null");
331  $sql.= ", ".(!empty($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null");
332  $sql.= ", ".(!empty($this->model_pdf)?"'".$this->db->escape($this->model_pdf)."'":"null");
333  $sql.= ", ".(int) $this->fk_incoterms;
334  $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
335  $sql.= ")";
336 
337  dol_syslog(get_class($this)."::create", LOG_DEBUG);
338  $resql=$this->db->query($sql);
339  if ($resql)
340  {
341  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."expedition");
342 
343  $sql = "UPDATE ".MAIN_DB_PREFIX."expedition";
344  $sql.= " SET ref = '(PROV".$this->id.")'";
345  $sql.= " WHERE rowid = ".$this->id;
346 
347  dol_syslog(get_class($this)."::create", LOG_DEBUG);
348  if ($this->db->query($sql))
349  {
350  // Insertion des lignes
351  $num=count($this->lines);
352  for ($i = 0; $i < $num; $i++)
353  {
354  if (! isset($this->lines[$i]->detail_batch))
355  { // no batch management
356  if (! $this->create_line($this->lines[$i]->entrepot_id, $this->lines[$i]->origin_line_id, $this->lines[$i]->qty, $this->lines[$i]->array_options) > 0)
357  {
358  $error++;
359  }
360  }
361  else
362  { // with batch management
363  if (! $this->create_line_batch($this->lines[$i],$this->lines[$i]->array_options) > 0)
364  {
365  $error++;
366  }
367  }
368  }
369 
370  if (! $error && $this->id && $this->origin_id)
371  {
372  $ret = $this->add_object_linked();
373  if (!$ret)
374  {
375  $error++;
376  }
377  }
378 
379  // Actions on extra fields
380  if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))
381  {
382  $result=$this->insertExtraFields();
383  if ($result < 0)
384  {
385  $error++;
386  }
387  }
388 
389  if (! $error && ! $notrigger)
390  {
391  // Call trigger
392  $result=$this->call_trigger('SHIPPING_CREATE',$user);
393  if ($result < 0) { $error++; }
394  // End call triggers
395 
396  if (! $error)
397  {
398  $this->db->commit();
399  return $this->id;
400  }
401  else
402  {
403  foreach($this->errors as $errmsg)
404  {
405  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
406  $this->error.=($this->error?', '.$errmsg:$errmsg);
407  }
408  $this->db->rollback();
409  return -1*$error;
410  }
411  }
412  else
413  {
414  $error++;
415  $this->error=$this->db->lasterror()." - sql=$sql";
416  $this->db->rollback();
417  return -3;
418  }
419  }
420  else
421  {
422  $error++;
423  $this->error=$this->db->lasterror()." - sql=$sql";
424  $this->db->rollback();
425  return -2;
426  }
427  }
428  else
429  {
430  $error++;
431  $this->error=$this->db->error()." - sql=$sql";
432  $this->db->rollback();
433  return -1;
434  }
435  }
436 
437  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
447  function create_line($entrepot_id, $origin_line_id, $qty,$array_options=0)
448  {
449  //phpcs:enable
450  $expeditionline = new ExpeditionLigne($this->db);
451  $expeditionline->fk_expedition = $this->id;
452  $expeditionline->entrepot_id = $entrepot_id;
453  $expeditionline->fk_origin_line = $origin_line_id;
454  $expeditionline->qty = $qty;
455  $expeditionline->array_options = $array_options;
456 
457  if (($lineId = $expeditionline->insert()) < 0)
458  {
459  $this->errors[]=$expeditionline->error;
460  }
461  return $lineId;
462  }
463 
464 
465  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
473  function create_line_batch($line_ext,$array_options=0)
474  {
475  // phpcs:enable
476  $error = 0;
477  $stockLocationQty = array(); // associated array with batch qty in stock location
478 
479  $tab=$line_ext->detail_batch;
480  // create stockLocation Qty array
481  foreach ($tab as $detbatch)
482  {
483  if ($detbatch->entrepot_id)
484  {
485  $stockLocationQty[$detbatch->entrepot_id] += $detbatch->qty;
486  }
487  }
488  // create shipment lines
489  foreach ($stockLocationQty as $stockLocation => $qty)
490  {
491  if (($line_id = $this->create_line($stockLocation,$line_ext->origin_line_id,$qty,$array_options)) < 0)
492  {
493  $error++;
494  }
495  else
496  {
497  // create shipment batch lines for stockLocation
498  foreach ($tab as $detbatch)
499  {
500  if ($detbatch->entrepot_id == $stockLocation){
501  if (! ($detbatch->create($line_id) >0)) // Create an expeditionlinebatch
502  {
503  $error++;
504  }
505  }
506  }
507  }
508  }
509 
510  if (! $error) return 1;
511  else return -1;
512  }
513 
523  function fetch($id, $ref='', $ref_ext='', $ref_int='')
524  {
525  global $conf;
526 
527  // Check parameters
528  if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
529 
530  $sql = "SELECT e.rowid, e.ref, e.fk_soc as socid, e.date_creation, e.ref_customer, e.ref_ext, e.ref_int, e.fk_user_author, e.fk_statut, e.fk_projet, e.billed";
531  $sql.= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
532  $sql.= ", e.date_expedition as date_expedition, e.model_pdf, e.fk_address, e.date_delivery";
533  $sql.= ", e.fk_shipping_method, e.tracking_number";
534  $sql.= ", e.note_private, e.note_public";
535  $sql.= ', e.fk_incoterms, e.location_incoterms';
536  $sql.= ', i.libelle as libelle_incoterms';
537  $sql.= ', s.libelle as shipping_method';
538  $sql.= ", el.fk_source as origin_id, el.sourcetype as origin";
539  $sql.= " FROM ".MAIN_DB_PREFIX."expedition as e";
540  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
541  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
542  $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_shipment_mode as s ON e.fk_shipping_method = s.rowid';
543  $sql.= " WHERE e.entity IN (".getEntity('expedition').")";
544  if ($id) $sql.= " AND e.rowid=".$id;
545  if ($ref) $sql.= " AND e.ref='".$this->db->escape($ref)."'";
546  if ($ref_ext) $sql.= " AND e.ref_ext='".$this->db->escape($ref_ext)."'";
547  if ($ref_int) $sql.= " AND e.ref_int='".$this->db->escape($ref_int)."'";
548 
549  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
550  $result = $this->db->query($sql);
551  if ($result)
552  {
553  if ($this->db->num_rows($result))
554  {
555  $obj = $this->db->fetch_object($result);
556 
557  $this->id = $obj->rowid;
558  $this->ref = $obj->ref;
559  $this->socid = $obj->socid;
560  $this->ref_customer = $obj->ref_customer;
561  $this->ref_ext = $obj->ref_ext;
562  $this->ref_int = $obj->ref_int;
563  $this->statut = $obj->fk_statut;
564  $this->user_author_id = $obj->fk_user_author;
565  $this->date_creation = $this->db->jdate($obj->date_creation);
566  $this->date = $this->db->jdate($obj->date_expedition); // TODO deprecated
567  $this->date_expedition = $this->db->jdate($obj->date_expedition); // TODO deprecated
568  $this->date_shipping = $this->db->jdate($obj->date_expedition); // Date real
569  $this->date_delivery = $this->db->jdate($obj->date_delivery); // Date planed
570  $this->fk_delivery_address = $obj->fk_address;
571  $this->modelpdf = $obj->model_pdf;
572  $this->shipping_method_id = $obj->fk_shipping_method;
573  $this->shipping_method = $obj->shipping_method;
574  $this->tracking_number = $obj->tracking_number;
575  $this->origin = ($obj->origin?$obj->origin:'commande'); // For compatibility
576  $this->origin_id = $obj->origin_id;
577  $this->billed = $obj->billed;
578  $this->fk_project = $obj->fk_projet;
579 
580  $this->trueWeight = $obj->weight;
581  $this->weight_units = $obj->weight_units;
582 
583  $this->trueWidth = $obj->width;
584  $this->width_units = $obj->size_units;
585  $this->trueHeight = $obj->height;
586  $this->height_units = $obj->size_units;
587  $this->trueDepth = $obj->size;
588  $this->depth_units = $obj->size_units;
589 
590  $this->note_public = $obj->note_public;
591  $this->note_private = $obj->note_private;
592 
593  // A denormalized value
594  $this->trueSize = $obj->size."x".$obj->width."x".$obj->height;
595  $this->size_units = $obj->size_units;
596 
597  //Incoterms
598  $this->fk_incoterms = $obj->fk_incoterms;
599  $this->location_incoterms = $obj->location_incoterms;
600  $this->libelle_incoterms = $obj->libelle_incoterms;
601 
602  $this->db->free($result);
603 
604  if ($this->statut == 0) $this->brouillon = 1;
605 
606  // Tracking url
607  $this->GetUrlTrackingStatus($obj->tracking_number);
608 
609  /*
610  * Thirparty
611  */
612  $result=$this->fetch_thirdparty();
613 
614  // Retreive extrafields
615  $this->fetch_optionals();
616 
617  /*
618  * Lines
619  */
620  $result=$this->fetch_lines();
621  if ($result < 0)
622  {
623  return -3;
624  }
625 
626  return 1;
627  }
628  else
629  {
630  dol_syslog(get_class($this).'::Fetch no expedition found', LOG_ERR);
631  $this->error='Delivery with id '.$id.' not found';
632  return 0;
633  }
634  }
635  else
636  {
637  $this->error=$this->db->error();
638  return -1;
639  }
640  }
641 
649  function valid($user, $notrigger=0)
650  {
651  global $conf, $langs;
652 
653  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
654 
655  dol_syslog(get_class($this)."::valid");
656 
657  // Protection
658  if ($this->statut)
659  {
660  dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
661  return 0;
662  }
663 
664  if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->creer))
665  || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->shipping_advance->validate))))
666  {
667  $this->error='Permission denied';
668  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
669  return -1;
670  }
671 
672  $this->db->begin();
673 
674  $error = 0;
675 
676  // Define new ref
677  $soc = new Societe($this->db);
678  $soc->fetch($this->socid);
679 
680  // Class of company linked to order
681  $result=$soc->set_as_client();
682 
683  // Define new ref
684  if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
685  {
686  $numref = $this->getNextNumRef($soc);
687  }
688  else
689  {
690  $numref = "EXP".$this->id;
691  }
692  $this->newref = $numref;
693 
694  $now=dol_now();
695 
696  // Validate
697  $sql = "UPDATE ".MAIN_DB_PREFIX."expedition SET";
698  $sql.= " ref='".$numref."'";
699  $sql.= ", fk_statut = 1";
700  $sql.= ", date_valid = '".$this->db->idate($now)."'";
701  $sql.= ", fk_user_valid = ".$user->id;
702  $sql.= " WHERE rowid = ".$this->id;
703 
704  dol_syslog(get_class($this)."::valid update expedition", LOG_DEBUG);
705  $resql=$this->db->query($sql);
706  if (! $resql)
707  {
708  $this->error=$this->db->lasterror();
709  $error++;
710  }
711 
712  // If stock increment is done on sending (recommanded choice)
713  if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT))
714  {
715  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
716 
717  $langs->load("agenda");
718 
719  // Loop on each product line to add a stock movement
720  $sql = "SELECT cd.fk_product, cd.subprice,";
721  $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
722  $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
723  $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
724  $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
725  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
726  $sql.= " WHERE ed.fk_expedition = ".$this->id;
727  $sql.= " AND cd.rowid = ed.fk_origin_line";
728 
729  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
730  $resql=$this->db->query($sql);
731  if ($resql)
732  {
733  $cpt = $this->db->num_rows($resql);
734  for ($i = 0; $i < $cpt; $i++)
735  {
736  $obj = $this->db->fetch_object($resql);
737  if (empty($obj->edbrowid))
738  {
739  $qty = $obj->qty;
740  }
741  else
742  {
743  $qty = $obj->edbqty;
744  }
745  if ($qty <= 0) continue;
746  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
747 
748  //var_dump($this->lines[$i]);
749  $mouvS = new MouvementStock($this->db);
750  $mouvS->origin = &$this;
751 
752  if (empty($obj->edbrowid))
753  {
754  // line without batch detail
755 
756  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
757  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref));
758  if ($result < 0) {
759  $error++;
760  $this->errors[]=$mouvS->error;
761  $this->errors = array_merge($this->errors, $mouvS->errors);
762  break;
763  }
764  }
765  else
766  {
767  // line with batch detail
768 
769  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
770  // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
771  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
772  if ($result < 0) {
773  $error++;
774  $this->errors[]=$mouvS->error;
775  $this->errors = array_merge($this->errors, $mouvS->errors);
776  break;
777  }
778  }
779  }
780  }
781  else
782  {
783  $this->db->rollback();
784  $this->error=$this->db->error();
785  return -2;
786  }
787  }
788 
789  // Change status of order to "shipment in process"
790  $ret = $this->setStatut(Commande::STATUS_SHIPMENTONPROCESS, $this->origin_id, $this->origin);
791 
792  if (! $ret)
793  {
794  $error++;
795  }
796 
797  if (! $error && ! $notrigger)
798  {
799  // Call trigger
800  $result=$this->call_trigger('SHIPPING_VALIDATE',$user);
801  if ($result < 0) { $error++; }
802  // End call triggers
803  }
804 
805  if (! $error)
806  {
807  $this->oldref = $this->ref;
808 
809  // Rename directory if dir was a temporary ref
810  if (preg_match('/^[\(]?PROV/i', $this->ref))
811  {
812  // On renomme repertoire ($this->ref = ancienne ref, $numfa = nouvelle ref)
813  // in order not to lose the attached files
814  $oldref = dol_sanitizeFileName($this->ref);
815  $newref = dol_sanitizeFileName($numref);
816  $dirsource = $conf->expedition->dir_output.'/sending/'.$oldref;
817  $dirdest = $conf->expedition->dir_output.'/sending/'.$newref;
818  if (file_exists($dirsource))
819  {
820  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
821 
822  if (@rename($dirsource, $dirdest))
823  {
824  dol_syslog("Rename ok");
825  // Rename docs starting with $oldref with $newref
826  $listoffiles=dol_dir_list($conf->expedition->dir_output.'/sending/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
827  foreach($listoffiles as $fileentry)
828  {
829  $dirsource=$fileentry['name'];
830  $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
831  $dirsource=$fileentry['path'].'/'.$dirsource;
832  $dirdest=$fileentry['path'].'/'.$dirdest;
833  @rename($dirsource, $dirdest);
834  }
835  }
836  }
837  }
838  }
839 
840  // Set new ref and current status
841  if (! $error)
842  {
843  $this->ref = $numref;
844  $this->statut = 1;
845  }
846 
847  if (! $error)
848  {
849  $this->db->commit();
850  return 1;
851  }
852  else
853  {
854  foreach($this->errors as $errmsg)
855  {
856  dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
857  $this->error.=($this->error?', '.$errmsg:$errmsg);
858  }
859  $this->db->rollback();
860  return -1*$error;
861  }
862  }
863 
864 
865  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
872  function create_delivery($user)
873  {
874  // phpcs:enable
875  global $conf;
876 
877  if ($conf->livraison_bon->enabled)
878  {
879  if ($this->statut == 1 || $this->statut == 2)
880  {
881  // Expedition validee
882  include_once DOL_DOCUMENT_ROOT.'/livraison/class/livraison.class.php';
883  $delivery = new Livraison($this->db);
884  $result=$delivery->create_from_sending($user, $this->id);
885  if ($result > 0)
886  {
887  return $result;
888  }
889  else
890  {
891  $this->error=$delivery->error;
892  return $result;
893  }
894  }
895  else return 0;
896  }
897  else return 0;
898  }
899 
911  function addline($entrepot_id, $id, $qty,$array_options=0)
912  {
913  global $conf, $langs;
914 
915  $num = count($this->lines);
916  $line = new ExpeditionLigne($this->db);
917 
918  $line->entrepot_id = $entrepot_id;
919  $line->origin_line_id = $id;
920  $line->qty = $qty;
921 
922  $orderline = new OrderLine($this->db);
923  $orderline->fetch($id);
924 
925  if (! empty($conf->stock->enabled) && ! empty($orderline->fk_product))
926  {
927  $fk_product = $orderline->fk_product;
928 
929  if (! ($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_SHIPMENTS))
930  {
931  $langs->load("errors");
932  $this->error=$langs->trans("ErrorWarehouseRequiredIntoShipmentLine");
933  return -1;
934  }
935 
936  if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT)
937  {
938  // Check must be done for stock of product into warehouse if $entrepot_id defined
939  $product=new Product($this->db);
940  $result=$product->fetch($fk_product);
941 
942  if ($entrepot_id > 0) {
943  $product->load_stock('warehouseopen');
944  $product_stock = $product->stock_warehouse[$entrepot_id]->real;
945  }
946  else
947  $product_stock = $product->stock_reel;
948 
949  $product_type=$product->type;
950  if ($product_type == 0 && $product_stock < $qty)
951  {
952  $langs->load("errors");
953  $this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $product->ref);
954  $this->db->rollback();
955  return -3;
956  }
957  }
958  }
959 
960  // If product need a batch number, we should not have called this function but addline_batch instead.
961  if (! empty($conf->productbatch->enabled) && ! empty($orderline->fk_product) && ! empty($orderline->product_tobatch))
962  {
963  $this->error='ADDLINE_WAS_CALLED_INSTEAD_OF_ADDLINEBATCH';
964  return -4;
965  }
966 
967  // extrafields
968  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
969  $line->array_options = $array_options;
970 
971  $this->lines[$num] = $line;
972  }
973 
974  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
982  function addline_batch($dbatch,$array_options=0)
983  {
984  // phpcs:enable
985  global $conf,$langs;
986 
987  $num = count($this->lines);
988  if ($dbatch['qty']>0)
989  {
990  $line = new ExpeditionLigne($this->db);
991  $tab=array();
992  foreach ($dbatch['detail'] as $key=>$value)
993  {
994  if ($value['q']>0)
995  {
996  // $value['q']=qty to move
997  // $value['id_batch']=id into llx_product_batch of record to move
998  //var_dump($value);
999 
1000  $linebatch = new ExpeditionLineBatch($this->db);
1001  $ret=$linebatch->fetchFromStock($value['id_batch']); // load serial, sellby, eatby
1002  if ($ret<0)
1003  {
1004  $this->error=$linebatch->error;
1005  return -1;
1006  }
1007  $linebatch->qty=$value['q'];
1008  $tab[]=$linebatch;
1009 
1010  if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT)
1011  {
1012  require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
1013  $prod_batch = new Productbatch($this->db);
1014  $prod_batch->fetch($value['id_batch']);
1015 
1016  if ($prod_batch->qty < $linebatch->qty)
1017  {
1018  $langs->load("errors");
1019  $this->errors[]=$langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $prod_batch->fk_product);
1020  dol_syslog(get_class($this)."::addline_batch error=Product ".$prod_batch->batch.": ".$this->errorsToString(), LOG_ERR);
1021  $this->db->rollback();
1022  return -1;
1023  }
1024  }
1025 
1026  //var_dump($linebatch);
1027  }
1028  }
1029  $line->entrepot_id = $linebatch->entrepot_id;
1030  $line->origin_line_id = $dbatch['ix_l'];
1031  $line->qty = $dbatch['qty'];
1032  $line->detail_batch=$tab;
1033 
1034  // extrafields
1035  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1036  $line->array_options = $array_options;
1037 
1038  //var_dump($line);
1039  $this->lines[$num] = $line;
1040  return 1;
1041  }
1042  }
1043 
1051  function update($user=null, $notrigger=0)
1052  {
1053  global $conf;
1054  $error=0;
1055 
1056  // Clean parameters
1057 
1058  if (isset($this->ref)) $this->ref=trim($this->ref);
1059  if (isset($this->entity)) $this->entity=trim($this->entity);
1060  if (isset($this->ref_customer)) $this->ref_customer=trim($this->ref_customer);
1061  if (isset($this->socid)) $this->socid=trim($this->socid);
1062  if (isset($this->fk_user_author)) $this->fk_user_author=trim($this->fk_user_author);
1063  if (isset($this->fk_user_valid)) $this->fk_user_valid=trim($this->fk_user_valid);
1064  if (isset($this->fk_delivery_address)) $this->fk_delivery_address=trim($this->fk_delivery_address);
1065  if (isset($this->shipping_method_id)) $this->shipping_method_id=trim($this->shipping_method_id);
1066  if (isset($this->tracking_number)) $this->tracking_number=trim($this->tracking_number);
1067  if (isset($this->statut)) $this->statut=(int) $this->statut;
1068  if (isset($this->trueDepth)) $this->trueDepth=trim($this->trueDepth);
1069  if (isset($this->trueWidth)) $this->trueWidth=trim($this->trueWidth);
1070  if (isset($this->trueHeight)) $this->trueHeight=trim($this->trueHeight);
1071  if (isset($this->size_units)) $this->size_units=trim($this->size_units);
1072  if (isset($this->weight_units)) $this->weight_units=trim($this->weight_units);
1073  if (isset($this->trueWeight)) $this->weight=trim($this->trueWeight);
1074  if (isset($this->note_private)) $this->note=trim($this->note_private);
1075  if (isset($this->note_public)) $this->note=trim($this->note_public);
1076  if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1077 
1078 
1079 
1080  // Check parameters
1081  // Put here code to add control on parameters values
1082 
1083  // Update request
1084  $sql = "UPDATE ".MAIN_DB_PREFIX."expedition SET";
1085 
1086  $sql.= " tms=".(dol_strlen($this->tms)!=0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1087  $sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1088  $sql.= " ref_customer=".(isset($this->ref_customer)?"'".$this->db->escape($this->ref_customer)."'":"null").",";
1089  $sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1090  $sql.= " date_creation=".(dol_strlen($this->date_creation)!=0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1091  $sql.= " fk_user_author=".(isset($this->fk_user_author)?$this->fk_user_author:"null").",";
1092  $sql.= " date_valid=".(dol_strlen($this->date_valid)!=0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1093  $sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->fk_user_valid:"null").",";
1094  $sql.= " date_expedition=".(dol_strlen($this->date_expedition)!=0 ? "'".$this->db->idate($this->date_expedition)."'" : 'null').",";
1095  $sql.= " date_delivery=".(dol_strlen($this->date_delivery)!=0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1096  $sql.= " fk_address=".(isset($this->fk_delivery_address)?$this->fk_delivery_address:"null").",";
1097  $sql.= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0)?$this->shipping_method_id:"null").",";
1098  $sql.= " tracking_number=".(isset($this->tracking_number)?"'".$this->db->escape($this->tracking_number)."'":"null").",";
1099  $sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1100  $sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1101  $sql.= " height=".(($this->trueHeight != '')?$this->trueHeight:"null").",";
1102  $sql.= " width=".(($this->trueWidth != '')?$this->trueWidth:"null").",";
1103  $sql.= " size_units=".(isset($this->size_units)?$this->size_units:"null").",";
1104  $sql.= " size=".(($this->trueDepth != '')?$this->trueDepth:"null").",";
1105  $sql.= " weight_units=".(isset($this->weight_units)?$this->weight_units:"null").",";
1106  $sql.= " weight=".(($this->trueWeight != '')?$this->trueWeight:"null").",";
1107  $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1108  $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1109  $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1110  $sql.= " entity=".$conf->entity;
1111 
1112  $sql.= " WHERE rowid=".$this->id;
1113 
1114  $this->db->begin();
1115 
1116  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1117  $resql = $this->db->query($sql);
1118  if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
1119 
1120  if (! $error)
1121  {
1122  if (! $notrigger)
1123  {
1124  // Call trigger
1125  $result=$this->call_trigger('SHIPPING_MODIFY',$user);
1126  if ($result < 0) { $error++; }
1127  // End call triggers
1128  }
1129  }
1130 
1131  // Commit or rollback
1132  if ($error)
1133  {
1134  foreach($this->errors as $errmsg)
1135  {
1136  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1137  $this->error.=($this->error?', '.$errmsg:$errmsg);
1138  }
1139  $this->db->rollback();
1140  return -1*$error;
1141  }
1142  else
1143  {
1144  $this->db->commit();
1145  return 1;
1146  }
1147  }
1148 
1155  function delete()
1156  {
1157  global $conf, $langs, $user;
1158 
1159  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1160  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
1161 
1162  $error=0;
1163  $this->error='';
1164 
1165  $this->db->begin();
1166 
1167  // Add a protection to refuse deleting if shipment has at least one delivery
1168  $this->fetchObjectLinked($this->id, 'shipping', 0, 'delivery'); // Get deliveries linked to this shipment
1169  if (count($this->linkedObjectsIds) > 0)
1170  {
1171  $this->error='ErrorThereIsSomeDeliveries';
1172  $error++;
1173  }
1174 
1175  if (! $error)
1176  {
1177  if (! $notrigger)
1178  {
1179  // Call trigger
1180  $result=$this->call_trigger('SHIPPING_DELETE',$user);
1181  if ($result < 0) { $error++; }
1182  // End call triggers
1183  }
1184  }
1185 
1186  // Stock control
1187  if (! $error && $conf->stock->enabled && $conf->global->STOCK_CALCULATE_ON_SHIPMENT && $this->statut > 0)
1188  {
1189  require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1190 
1191  $langs->load("agenda");
1192 
1193  // Loop on each product line to add a stock movement
1194  $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
1195  $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
1196  $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
1197  $sql.= " WHERE ed.fk_expedition = ".$this->id;
1198  $sql.= " AND cd.rowid = ed.fk_origin_line";
1199 
1200  dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1201  $resql=$this->db->query($sql);
1202  if ($resql)
1203  {
1204  $cpt = $this->db->num_rows($resql);
1205  for ($i = 0; $i < $cpt; $i++)
1206  {
1207  dol_syslog(get_class($this)."::delete movement index ".$i);
1208  $obj = $this->db->fetch_object($resql);
1209 
1210  $mouvS = new MouvementStock($this->db);
1211  // we do not log origin because it will be deleted
1212  $mouvS->origin = null;
1213  // get lot/serial
1214  $lotArray = null;
1215  if ($conf->productbatch->enabled)
1216  {
1217  $lotArray = ExpeditionLineBatch::fetchAll($this->db,$obj->expeditiondet_id);
1218  if (! is_array($lotArray))
1219  {
1220  $error++;$this->errors[]="Error ".$this->db->lasterror();
1221  }
1222  }
1223  if (empty($lotArray)) {
1224  // no lot/serial
1225  // We increment stock of product (and sub-products)
1226  // We use warehouse selected for each line
1227  $result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref)); // Price is set to 0, because we don't want to see WAP changed
1228  if ($result < 0)
1229  {
1230  $error++;$this->errors=$this->errors + $mouvS->errors;
1231  break;
1232  }
1233  }
1234  else
1235  {
1236  // We increment stock of batches
1237  // We use warehouse selected for each line
1238  foreach($lotArray as $lot)
1239  {
1240  $result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref), $lot->eatby, $lot->sellby, $lot->batch); // Price is set to 0, because we don't want to see WAP changed
1241  if ($result < 0)
1242  {
1243  $error++;$this->errors=$this->errors + $mouvS->errors;
1244  break;
1245  }
1246  }
1247  if ($error) break; // break for loop incase of error
1248  }
1249  }
1250  }
1251  else
1252  {
1253  $error++;$this->errors[]="Error ".$this->db->lasterror();
1254  }
1255  }
1256 
1257  // delete batch expedition line
1258  if (! $error && $conf->productbatch->enabled)
1259  {
1260  if (ExpeditionLineBatch::deletefromexp($this->db,$this->id) < 0)
1261  {
1262  $error++;$this->errors[]="Error ".$this->db->lasterror();
1263  }
1264  }
1265 
1266  if (! $error)
1267  {
1268  $sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet";
1269  $sql.= " WHERE fk_expedition = ".$this->id;
1270 
1271  if ( $this->db->query($sql) )
1272  {
1273  // Delete linked object
1274  $res = $this->deleteObjectLinked();
1275  if ($res < 0) $error++;
1276 
1277  if (! $error)
1278  {
1279  $sql = "DELETE FROM ".MAIN_DB_PREFIX."expedition";
1280  $sql.= " WHERE rowid = ".$this->id;
1281 
1282  if ($this->db->query($sql))
1283  {
1284  if (! empty($this->origin) && $this->origin_id > 0)
1285  {
1286  $this->fetch_origin();
1287  $origin=$this->origin;
1288  if ($this->$origin->statut == Commande::STATUS_SHIPMENTONPROCESS) // If order source of shipment is "shipment in progress"
1289  {
1290  // Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
1291  $this->$origin->loadExpeditions();
1292  //var_dump($this->$origin->expeditions);exit;
1293  if (count($this->$origin->expeditions) <= 0)
1294  {
1295  $this->$origin->setStatut(Commande::STATUS_VALIDATED);
1296  }
1297  }
1298  }
1299 
1300  if (! $error)
1301  {
1302  $this->db->commit();
1303 
1304  // We delete PDFs
1305  $ref = dol_sanitizeFileName($this->ref);
1306  if (! empty($conf->expedition->dir_output))
1307  {
1308  $dir = $conf->expedition->dir_output . '/sending/' . $ref ;
1309  $file = $dir . '/' . $ref . '.pdf';
1310  if (file_exists($file))
1311  {
1312  if (! dol_delete_file($file))
1313  {
1314  return 0;
1315  }
1316  }
1317  if (file_exists($dir))
1318  {
1319  if (!dol_delete_dir_recursive($dir))
1320  {
1321  $this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
1322  return 0;
1323  }
1324  }
1325  }
1326 
1327  return 1;
1328  }
1329  else
1330  {
1331  $this->db->rollback();
1332  return -1;
1333  }
1334  }
1335  else
1336  {
1337  $this->error=$this->db->lasterror()." - sql=$sql";
1338  $this->db->rollback();
1339  return -3;
1340  }
1341  }
1342  else
1343  {
1344  $this->error=$this->db->lasterror()." - sql=$sql";
1345  $this->db->rollback();
1346  return -2;
1347  }
1348  }
1349  else
1350  {
1351  $this->error=$this->db->lasterror()." - sql=$sql";
1352  $this->db->rollback();
1353  return -1;
1354  }
1355  }
1356  else
1357  {
1358  $this->db->rollback();
1359  return -1;
1360  }
1361  }
1362 
1363  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1369  function fetch_lines()
1370  {
1371  // phpcs:enable
1372  global $conf, $mysoc;
1373  // TODO: recuperer les champs du document associe a part
1374 
1375  $sql = "SELECT cd.rowid, cd.fk_product, cd.label as custom_label, cd.description, cd.qty as qty_asked, cd.product_type";
1376  $sql.= ", cd.total_ht, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.total_tva";
1377  $sql.= ", cd.vat_src_code, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.info_bits, cd.price, cd.subprice, cd.remise_percent,cd.buy_price_ht as pa_ht";
1378  $sql.= ", cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc";
1379  $sql.= ", ed.rowid as line_id, ed.qty as qty_shipped, ed.fk_origin_line, ed.fk_entrepot";
1380  $sql.= ", p.ref as product_ref, p.label as product_label, p.fk_product_type";
1381  $sql.= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tobatch as product_tobatch";
1382  $sql.= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed, ".MAIN_DB_PREFIX."commandedet as cd";
1383  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
1384  $sql.= " WHERE ed.fk_expedition = ".$this->id;
1385  $sql.= " AND ed.fk_origin_line = cd.rowid";
1386  $sql.= " ORDER BY cd.rang, ed.fk_origin_line";
1387 
1388  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1389  $resql = $this->db->query($sql);
1390  if ($resql)
1391  {
1392  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1393 
1394  $num = $this->db->num_rows($resql);
1395  $i = 0;
1396  $lineindex = 0;
1397  $originline = 0;
1398 
1399  $this->total_ht = 0;
1400  $this->total_tva = 0;
1401  $this->total_ttc = 0;
1402  $this->total_localtax1 = 0;
1403  $this->total_localtax2 = 0;
1404 
1405  while ($i < $num)
1406  {
1407  $obj = $this->db->fetch_object($resql);
1408 
1409  if ($originline == $obj->fk_origin_line) {
1410  $line->entrepot_id = 0; // entrepod_id in details_entrepot
1411  $line->qty_shipped += $obj->qty_shipped;
1412  } else {
1413  $line = new ExpeditionLigne($this->db);
1414  $line->entrepot_id = $obj->fk_entrepot;
1415  $line->qty_shipped = $obj->qty_shipped;
1416  }
1417 
1418  $detail_entrepot = new stdClass;
1419  $detail_entrepot->entrepot_id = $obj->fk_entrepot;
1420  $detail_entrepot->qty_shipped = $obj->qty_shipped;
1421  $detail_entrepot->line_id = $obj->line_id;
1422  $line->details_entrepot[] = $detail_entrepot;
1423 
1424  $line->line_id = $obj->line_id;
1425  $line->rowid = $obj->line_id; // TODO deprecated
1426  $line->id = $obj->line_id;
1427 
1428  $line->fk_origin = 'orderline';
1429  $line->fk_origin_line = $obj->fk_origin_line;
1430  $line->origin_line_id = $obj->fk_origin_line; // TODO deprecated
1431 
1432  $line->fk_expedition = $this->id; // id of parent
1433 
1434  $line->product_type = $obj->product_type;
1435  $line->fk_product = $obj->fk_product;
1436  $line->fk_product_type = $obj->fk_product_type;
1437  $line->ref = $obj->product_ref; // TODO deprecated
1438  $line->product_ref = $obj->product_ref;
1439  $line->product_label = $obj->product_label;
1440  $line->libelle = $obj->product_label; // TODO deprecated
1441  $line->product_tobatch = $obj->product_tobatch;
1442  $line->label = $obj->custom_label;
1443  $line->description = $obj->description;
1444  $line->qty_asked = $obj->qty_asked;
1445  $line->weight = $obj->weight;
1446  $line->weight_units = $obj->weight_units;
1447  $line->length = $obj->length;
1448  $line->length_units = $obj->length_units;
1449  $line->surface = $obj->surface;
1450  $line->surface_units = $obj->surface_units;
1451  $line->volume = $obj->volume;
1452  $line->volume_units = $obj->volume_units;
1453 
1454  $line->pa_ht = $obj->pa_ht;
1455 
1456  // Local taxes
1457  $localtax_array=array(0=>$obj->localtax1_type, 1=>$obj->localtax1_tx, 2=>$obj->localtax2_type, 3=>$obj->localtax2_tx);
1458  $localtax1_tx = get_localtax($obj->tva_tx, 1, $this->thirdparty);
1459  $localtax2_tx = get_localtax($obj->tva_tx, 2, $this->thirdparty);
1460 
1461  // For invoicing
1462  $tabprice = calcul_price_total($obj->qty_shipped, $obj->subprice, $obj->remise_percent, $obj->tva_tx, $localtax1_tx, $localtax2_tx, 0, 'HT', $obj->info_bits, $obj->fk_product_type, $mysoc, $localtax_array); // We force type to 0
1463  $line->desc = $obj->description; // We need ->desc because some code into CommonObject use desc (property defined for other elements)
1464  $line->qty = $line->qty_shipped;
1465  $line->total_ht = $tabprice[0];
1466  $line->total_localtax1 = $tabprice[9];
1467  $line->total_localtax2 = $tabprice[10];
1468  $line->total_ttc = $tabprice[2];
1469  $line->total_tva = $tabprice[1];
1470  $line->vat_src_code = $obj->vat_src_code;
1471  $line->tva_tx = $obj->tva_tx;
1472  $line->localtax1_tx = $obj->localtax1_tx;
1473  $line->localtax2_tx = $obj->localtax2_tx;
1474  $line->info_bits = $obj->info_bits;
1475  $line->price = $obj->price;
1476  $line->subprice = $obj->subprice;
1477  $line->remise_percent = $obj->remise_percent;
1478 
1479  $this->total_ht+= $tabprice[0];
1480  $this->total_tva+= $tabprice[1];
1481  $this->total_ttc+= $tabprice[2];
1482  $this->total_localtax1+= $tabprice[9];
1483  $this->total_localtax2+= $tabprice[10];
1484 
1485  // Multicurrency
1486  $this->fk_multicurrency = $obj->fk_multicurrency;
1487  $this->multicurrency_code = $obj->multicurrency_code;
1488  $this->multicurrency_subprice = $obj->multicurrency_subprice;
1489  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1490  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1491  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1492 
1493  if ($originline != $obj->fk_origin_line)
1494  {
1495  $line->detail_batch = array();
1496  }
1497 
1498  // Detail of batch
1499  if (! empty($conf->productbatch->enabled) && $obj->line_id > 0 && $obj->product_tobatch > 0)
1500  {
1501  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
1502 
1503  $newdetailbatch = ExpeditionLineBatch::fetchAll($this->db, $obj->line_id, $obj->fk_product);
1504  if (is_array($newdetailbatch))
1505  {
1506  if ($originline != $obj->fk_origin_line)
1507  {
1508  $line->detail_batch = $newdetailbatch;
1509  }
1510  else
1511  {
1512  $line->detail_batch = array_merge($line->detail_batch, $newdetailbatch);
1513  }
1514  }
1515  }
1516 
1517  if ($originline != $obj->fk_origin_line)
1518  {
1519  $this->lines[$lineindex] = $line;
1520  $lineindex++;
1521  }
1522  else
1523  {
1524  $line->total_ht += $tabprice[0];
1525  $line->total_localtax1 += $tabprice[9];
1526  $line->total_localtax2 += $tabprice[10];
1527  $line->total_ttc += $tabprice[2];
1528  $line->total_tva += $tabprice[1];
1529  }
1530 
1531  $i++;
1532  $originline = $obj->fk_origin_line;
1533  }
1534  $this->db->free($resql);
1535  return 1;
1536  }
1537  else
1538  {
1539  $this->error=$this->db->error();
1540  return -3;
1541  }
1542  }
1543 
1551  function deleteline($user, $lineid)
1552  {
1553  global $user;
1554 
1555  if ($this->statut == self::STATUS_DRAFT)
1556  {
1557  $this->db->begin();
1558 
1559  $line=new ExpeditionLigne($this->db);
1560 
1561  // For triggers
1562  $line->fetch($lineid);
1563 
1564  if ($line->delete($user) > 0)
1565  {
1566  //$this->update_price(1);
1567 
1568  $this->db->commit();
1569  return 1;
1570  }
1571  else
1572  {
1573  $this->db->rollback();
1574  return -1;
1575  }
1576  }
1577  else
1578  {
1579  $this->error='ErrorDeleteLineNotAllowedByObjectStatus';
1580  return -2;
1581  }
1582  }
1583 
1584 
1596  function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
1597  {
1598  global $langs;
1599 
1600  $result='';
1601  $label = '<u>' . $langs->trans("ShowSending") . '</u>';
1602  $label .= '<br><b>' . $langs->trans('Ref') . ':</b> '.$this->ref;
1603  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
1604 
1605  $url = DOL_URL_ROOT.'/expedition/card.php?id='.$this->id;
1606 
1607  if ($short) return $url;
1608 
1609  if ($option !== 'nolink')
1610  {
1611  // Add param to save lastsearch_values or not
1612  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1613  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1614  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1615  }
1616 
1617  $linkclose='';
1618  if (empty($notooltip))
1619  {
1620  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1621  {
1622  $label=$langs->trans("ShowSending");
1623  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1624  }
1625  $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1626  $linkclose.=' class="classfortooltip"';
1627  }
1628 
1629  $linkstart = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
1630  $linkend='</a>';
1631 
1632  $result .= $linkstart;
1633  if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1634  if ($withpicto != 2) $result.= $this->ref;
1635  $result .= $linkend;
1636 
1637  return $result;
1638  }
1639 
1646  function getLibStatut($mode=0)
1647  {
1648  return $this->LibStatut($this->statut,$mode);
1649  }
1650 
1651  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1659  function LibStatut($statut,$mode)
1660  {
1661  // phpcs:enable
1662  global $langs;
1663 
1664  if ($mode==0)
1665  {
1666  if ($statut==0) return $langs->trans($this->statuts[$statut]);
1667  elseif ($statut==1) return $langs->trans($this->statuts[$statut]);
1668  elseif ($statut==2) return $langs->trans($this->statuts[$statut]);
1669  }
1670  elseif ($mode==1)
1671  {
1672  if ($statut==0) return $langs->trans($this->statutshorts[$statut]);
1673  elseif ($statut==1) return $langs->trans($this->statutshorts[$statut]);
1674  elseif ($statut==2) return $langs->trans($this->statutshorts[$statut]);
1675  }
1676  elseif ($mode == 3)
1677  {
1678  if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0');
1679  elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4');
1680  elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]),'statut6');
1681  }
1682  elseif ($mode == 4)
1683  {
1684  if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0').' '.$langs->trans($this->statuts[$statut]);
1685  elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4').' '.$langs->trans($this->statuts[$statut]);
1686  elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]),'statut6').' '.$langs->trans($this->statuts[$statut]);
1687  }
1688  elseif ($mode == 5)
1689  {
1690  if ($statut==0) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut0');
1691  elseif ($statut==1) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut4');
1692  elseif ($statut==2) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut6');
1693  }
1694  }
1695 
1703  function initAsSpecimen()
1704  {
1705  global $langs;
1706 
1707  $now=dol_now();
1708 
1709  dol_syslog(get_class($this)."::initAsSpecimen");
1710 
1711  // Load array of products prodids
1712  $num_prods = 0;
1713  $prodids = array();
1714  $sql = "SELECT rowid";
1715  $sql.= " FROM ".MAIN_DB_PREFIX."product";
1716  $sql.= " WHERE entity IN (".getEntity('product').")";
1717  $resql = $this->db->query($sql);
1718  if ($resql)
1719  {
1720  $num_prods = $this->db->num_rows($resql);
1721  $i = 0;
1722  while ($i < $num_prods)
1723  {
1724  $i++;
1725  $row = $this->db->fetch_row($resql);
1726  $prodids[$i] = $row[0];
1727  }
1728  }
1729 
1730  $order=new Commande($this->db);
1731  $order->initAsSpecimen();
1732 
1733  // Initialise parametres
1734  $this->id=0;
1735  $this->ref = 'SPECIMEN';
1736  $this->specimen=1;
1737  $this->statut = 1;
1738  $this->livraison_id = 0;
1739  $this->date = $now;
1740  $this->date_creation = $now;
1741  $this->date_valid = $now;
1742  $this->date_delivery = $now;
1743  $this->date_expedition = $now + 24*3600;
1744 
1745  $this->entrepot_id = 0;
1746  $this->fk_delivery_address = 0;
1747  $this->socid = 1;
1748 
1749  $this->commande_id = 0;
1750  $this->commande = $order;
1751 
1752  $this->origin_id = 1;
1753  $this->origin = 'commande';
1754 
1755  $this->note_private = 'Private note';
1756  $this->note_public = 'Public note';
1757 
1758  $nbp = 5;
1759  $xnbp = 0;
1760  while ($xnbp < $nbp)
1761  {
1762  $line=new ExpeditionLigne($this->db);
1763  $line->desc=$langs->trans("Description")." ".$xnbp;
1764  $line->libelle=$langs->trans("Description")." ".$xnbp;
1765  $line->qty=10;
1766  $line->qty_asked=5;
1767  $line->qty_shipped=4;
1768  $line->fk_product=$this->commande->lines[$xnbp]->fk_product;
1769 
1770  $this->lines[]=$line;
1771  $xnbp++;
1772  }
1773  }
1774 
1775  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1783  function set_date_livraison($user, $date_livraison)
1784  {
1785  // phpcs:enable
1786  if ($user->rights->expedition->creer)
1787  {
1788  $sql = "UPDATE ".MAIN_DB_PREFIX."expedition";
1789  $sql.= " SET date_delivery = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
1790  $sql.= " WHERE rowid = ".$this->id;
1791 
1792  dol_syslog(get_class($this)."::set_date_livraison", LOG_DEBUG);
1793  $resql=$this->db->query($sql);
1794  if ($resql)
1795  {
1796  $this->date_delivery = $date_livraison;
1797  return 1;
1798  }
1799  else
1800  {
1801  $this->error=$this->db->error();
1802  return -1;
1803  }
1804  }
1805  else
1806  {
1807  return -2;
1808  }
1809  }
1810 
1811  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1818  {
1819  // phpcs:enable
1820  global $langs;
1821  $this->meths = array();
1822 
1823  $sql = "SELECT em.rowid, em.code, em.libelle";
1824  $sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1825  $sql.= " WHERE em.active = 1";
1826  $sql.= " ORDER BY em.libelle ASC";
1827 
1828  $resql = $this->db->query($sql);
1829  if ($resql)
1830  {
1831  while ($obj = $this->db->fetch_object($resql))
1832  {
1833  $label=$langs->trans('SendingMethod'.$obj->code);
1834  $this->meths[$obj->rowid] = ($label != 'SendingMethod'.$obj->code?$label:$obj->libelle);
1835  }
1836  }
1837  }
1838 
1839  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1846  function list_delivery_methods($id='')
1847  {
1848  // phpcs:enable
1849  global $langs;
1850 
1851  $this->listmeths = array();
1852  $i=0;
1853 
1854  $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1855  $sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1856  if ($id!='') $sql.= " WHERE em.rowid=".$id;
1857 
1858  $resql = $this->db->query($sql);
1859  if ($resql)
1860  {
1861  while ($obj = $this->db->fetch_object($resql))
1862  {
1863  $this->listmeths[$i]['rowid'] = $obj->rowid;
1864  $this->listmeths[$i]['code'] = $obj->code;
1865  $label=$langs->trans('SendingMethod'.$obj->code);
1866  $this->listmeths[$i]['libelle'] = ($label != 'SendingMethod'.$obj->code?$label:$obj->libelle);
1867  $this->listmeths[$i]['description'] = $obj->description;
1868  $this->listmeths[$i]['tracking'] = $obj->tracking;
1869  $this->listmeths[$i]['active'] = $obj->active;
1870  $i++;
1871  }
1872  }
1873  }
1874 
1875  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1883  function update_delivery_method($id='')
1884  {
1885  // phpcs:enable
1886  if ($id=='')
1887  {
1888  $sql = "INSERT INTO ".MAIN_DB_PREFIX."c_shipment_mode (code, libelle, description, tracking)";
1889  $sql.=" VALUES ('".$this->db->escape($this->update['code'])."','".$this->db->escape($this->update['libelle'])."','".$this->db->escape($this->update['description'])."','".$this->db->escape($this->update['tracking'])."')";
1890  $resql = $this->db->query($sql);
1891  }
1892  else
1893  {
1894  $sql = "UPDATE ".MAIN_DB_PREFIX."c_shipment_mode SET";
1895  $sql.= " code='".$this->db->escape($this->update['code'])."'";
1896  $sql.= ",libelle='".$this->db->escape($this->update['libelle'])."'";
1897  $sql.= ",description='".$this->db->escape($this->update['description'])."'";
1898  $sql.= ",tracking='".$this->db->escape($this->update['tracking'])."'";
1899  $sql.= " WHERE rowid=".$id;
1900  $resql = $this->db->query($sql);
1901  }
1902  if ($resql < 0) dol_print_error($this->db,'');
1903  }
1904 
1905  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1914  {
1915  // phpcs:enable
1916  $sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=1';
1917  $sql.= ' WHERE rowid='.$id;
1918 
1919  $resql = $this->db->query($sql);
1920  }
1921 
1922  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1931  {
1932  // phpcs:enable
1933  $sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=0';
1934  $sql.= ' WHERE rowid='.$id;
1935 
1936  $resql = $this->db->query($sql);
1937  }
1938 
1939 
1940  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1947  function GetUrlTrackingStatus($value='')
1948  {
1949  // phpcs:enable
1950  if (! empty($this->shipping_method_id))
1951  {
1952  $sql = "SELECT em.code, em.tracking";
1953  $sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1954  $sql.= " WHERE em.rowid = ".$this->shipping_method_id;
1955 
1956  $resql = $this->db->query($sql);
1957  if ($resql)
1958  {
1959  if ($obj = $this->db->fetch_object($resql))
1960  {
1961  $tracking = $obj->tracking;
1962  }
1963  }
1964  }
1965 
1966  if (!empty($tracking) && !empty($value))
1967  {
1968  $url = str_replace('{TRACKID}', $value, $tracking);
1969  $this->tracking_url = sprintf('<a target="_blank" href="%s">'.($value?$value:'url').'</a>',$url,$url);
1970  }
1971  else
1972  {
1973  $this->tracking_url = $value;
1974  }
1975  }
1976 
1982  function setClosed()
1983  {
1984  global $conf,$langs,$user;
1985 
1986  $error=0;
1987 
1988  $this->db->begin();
1989 
1990  $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut='.self::STATUS_CLOSED;
1991  $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
1992 
1993  $resql=$this->db->query($sql);
1994  if ($resql)
1995  {
1996  // Set order billed if 100% of order is shipped (qty in shipment lines match qty in order lines)
1997  if ($this->origin == 'commande' && $this->origin_id > 0)
1998  {
1999  $order = new Commande($this->db);
2000  $order->fetch($this->origin_id);
2001 
2002  $order->loadExpeditions(self::STATUS_CLOSED); // Fill $order->expeditions = array(orderlineid => qty)
2003 
2004  $shipments_match_order = 1;
2005  foreach($order->lines as $line)
2006  {
2007  $lineid = $line->id;
2008  $qty = $line->qty;
2009  if (($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) && $order->expeditions[$lineid] != $qty)
2010  {
2011  $shipments_match_order = 0;
2012  $text='Qty for order line id '.$lineid.' is '.$qty.'. However in the shipments with status Expedition::STATUS_CLOSED='.self::STATUS_CLOSED.' we have qty = '.$order->expeditions[$lineid].', so we can t close order';
2013  dol_syslog($text);
2014  break;
2015  }
2016  }
2017  if ($shipments_match_order)
2018  {
2019  dol_syslog("Qty for the ".count($order->lines)." lines of order have same value for shipments with status Expedition::STATUS_CLOSED=".self::STATUS_CLOSED.', so we close order');
2020  $order->cloture($user);
2021  }
2022  }
2023 
2024  $this->statut=self::STATUS_CLOSED;
2025 
2026 
2027  // If stock increment is done on closing
2028  if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE))
2029  {
2030  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2031 
2032  $langs->load("agenda");
2033 
2034  // Loop on each product line to add a stock movement
2035  // TODO possibilite d'expedier a partir d'une propale ou autre origine ?
2036  $sql = "SELECT cd.fk_product, cd.subprice,";
2037  $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
2038  $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
2039  $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
2040  $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
2041  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
2042  $sql.= " WHERE ed.fk_expedition = ".$this->id;
2043  $sql.= " AND cd.rowid = ed.fk_origin_line";
2044 
2045  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2046  $resql=$this->db->query($sql);
2047  if ($resql)
2048  {
2049  $cpt = $this->db->num_rows($resql);
2050  for ($i = 0; $i < $cpt; $i++)
2051  {
2052  $obj = $this->db->fetch_object($resql);
2053  if (empty($obj->edbrowid))
2054  {
2055  $qty = $obj->qty;
2056  }
2057  else
2058  {
2059  $qty = $obj->edbqty;
2060  }
2061  if ($qty <= 0) continue;
2062  dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2063 
2064  $mouvS = new MouvementStock($this->db);
2065  $mouvS->origin = &$this;
2066 
2067  if (empty($obj->edbrowid))
2068  {
2069  // line without batch detail
2070 
2071  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
2072  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$numref));
2073  if ($result < 0) {
2074  $this->error = $mouvS->error;
2075  $this->errors = $mouvS->errors;
2076  $error++; break;
2077  }
2078  }
2079  else
2080  {
2081  // line with batch detail
2082 
2083  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
2084  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
2085  if ($result < 0) {
2086  $this->error = $mouvS->error;
2087  $this->errors = $mouvS->errors;
2088  $error++; break;
2089  }
2090  }
2091  }
2092  }
2093  else
2094  {
2095  $this->error=$this->db->lasterror();
2096  $error++;
2097  }
2098  }
2099 
2100  // Call trigger
2101  if (! $error)
2102  {
2103  $result=$this->call_trigger('SHIPPING_CLOSED',$user);
2104  if ($result < 0) {
2105  $error++;
2106  }
2107  }
2108  }
2109  else
2110  {
2111  dol_print_error($this->db);
2112  $error++;
2113  }
2114 
2115  if (! $error)
2116  {
2117  $this->db->commit();
2118  return 1;
2119  }
2120  else
2121  {
2122  $this->db->rollback();
2123  return -1;
2124  }
2125  }
2126 
2127  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2133  function set_billed()
2134  {
2135  // phpcs:enable
2136  global $user;
2137  $error=0;
2138 
2139  $this->db->begin();
2140 
2141  $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=2, billed=1'; // TODO Update only billed
2142  $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
2143 
2144  $resql=$this->db->query($sql);
2145  if ($resql)
2146  {
2147  $this->statut=2;
2148  $this->billed=1;
2149 
2150  // Call trigger
2151  $result=$this->call_trigger('SHIPPING_BILLED',$user);
2152  if ($result < 0) {
2153  $error++;
2154  }
2155  } else {
2156  $error++;
2157  $this->errors[]=$this->db->lasterror;
2158  }
2159 
2160  if (empty($error)) {
2161  $this->db->commit();
2162  return 1;
2163  }
2164  else
2165  {
2166  $this->db->rollback();
2167  return -1;
2168  }
2169  }
2170 
2176  function reOpen()
2177  {
2178  global $conf,$langs,$user;
2179 
2180  $error=0;
2181 
2182  // Protection. This avoid to move stock later when we should not
2183  if ($this->statut == self::STATUS_VALIDATED)
2184  {
2185  return 0;
2186  }
2187 
2188  $this->db->begin();
2189 
2190  $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=1';
2191  $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
2192 
2193  $resql=$this->db->query($sql);
2194  if ($resql)
2195  {
2196  $this->statut=1;
2197  $this->billed=0;
2198 
2199  // If stock increment is done on closing
2200  if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE))
2201  {
2202  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2203 
2204  $langs->load("agenda");
2205 
2206  // Loop on each product line to add a stock movement
2207  // TODO possibilite d'expedier a partir d'une propale ou autre origine
2208  $sql = "SELECT cd.fk_product, cd.subprice,";
2209  $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
2210  $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
2211  $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
2212  $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
2213  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
2214  $sql.= " WHERE ed.fk_expedition = ".$this->id;
2215  $sql.= " AND cd.rowid = ed.fk_origin_line";
2216 
2217  dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2218  $resql=$this->db->query($sql);
2219  if ($resql)
2220  {
2221  $cpt = $this->db->num_rows($resql);
2222  for ($i = 0; $i < $cpt; $i++)
2223  {
2224  $obj = $this->db->fetch_object($resql);
2225  if (empty($obj->edbrowid))
2226  {
2227  $qty = $obj->qty;
2228  }
2229  else
2230  {
2231  $qty = $obj->edbqty;
2232  }
2233  if ($qty <= 0) continue;
2234  dol_syslog(get_class($this)."::reopen expedition movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2235 
2236  //var_dump($this->lines[$i]);
2237  $mouvS = new MouvementStock($this->db);
2238  $mouvS->origin = &$this;
2239 
2240  if (empty($obj->edbrowid))
2241  {
2242  // line without batch detail
2243 
2244  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
2245  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$numref));
2246  if ($result < 0) {
2247  $this->error = $mouvS->error;
2248  $this->errors = $mouvS->errors;
2249  $error++; break;
2250  }
2251  }
2252  else
2253  {
2254  // line with batch detail
2255 
2256  // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
2257  $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
2258  if ($result < 0) {
2259  $this->error = $mouvS->error;
2260  $this->errors = $mouvS->errors;
2261  $error++; break;
2262  }
2263  }
2264  }
2265  }
2266  else
2267  {
2268  $this->error=$this->db->lasterror();
2269  $error++;
2270  }
2271  }
2272 
2273  if (! $error)
2274  {
2275  // Call trigger
2276  $result=$this->call_trigger('SHIPPING_REOPEN',$user);
2277  if ($result < 0) {
2278  $error++;
2279  }
2280  }
2281  } else {
2282  $error++;
2283  $this->errors[]=$this->db->lasterror();
2284  }
2285 
2286  if (! $error)
2287  {
2288  $this->db->commit();
2289  return 1;
2290  }
2291  else
2292  {
2293  $this->db->rollback();
2294  return -1;
2295  }
2296  }
2297 
2309  public function generateDocument($modele, $outputlangs,$hidedetails=0, $hidedesc=0, $hideref=0,$moreparams=null)
2310  {
2311  global $conf,$langs;
2312 
2313  $langs->load("sendings");
2314 
2315  if (! dol_strlen($modele)) {
2316 
2317  $modele = 'rouget';
2318 
2319  if ($this->modelpdf) {
2320  $modele = $this->modelpdf;
2321  } elseif (! empty($conf->global->EXPEDITION_ADDON_PDF)) {
2322  $modele = $conf->global->EXPEDITION_ADDON_PDF;
2323  }
2324  }
2325 
2326  $modelpath = "core/modules/expedition/doc/";
2327 
2328  $this->fetch_origin();
2329 
2330  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref,$moreparams);
2331  }
2332 
2341  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2342  {
2343  $tables = array(
2344  'expedition'
2345  );
2346 
2347  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2348  }
2349 }
2350 
2351 
2356 {
2360  public $element='expeditiondet';
2361 
2365  public $table_element='expeditiondet';
2366 
2372 
2376  public $fk_origin_line;
2377 
2381  public $fk_expedition;
2382 
2386  public $db;
2387 
2391  public $qty;
2392 
2396  public $qty_shipped;
2397 
2401  public $fk_product;
2402  public $detail_batch;
2403 
2407  public $entrepot_id;
2408 
2409 
2413  public $qty_asked;
2414 
2419  public $ref;
2420 
2424  public $product_ref;
2425 
2430  public $libelle;
2431 
2435  public $product_label;
2436 
2442  public $desc;
2443 
2447  public $product_desc;
2448 
2452  public $weight;
2453  public $weight_units;
2454 
2458  public $length;
2459  public $length_units;
2460 
2464  public $surface;
2465  public $surface_units;
2466 
2470  public $volume;
2471  public $volume_units;
2472 
2473  // Invoicing
2474  public $remise_percent;
2475  public $tva_tx;
2476 
2480  public $total_ht;
2481 
2485  public $total_ttc;
2486 
2490  public $total_tva;
2491 
2495  public $total_localtax1;
2496 
2500  public $total_localtax2;
2501 
2502 
2508  function __construct($db)
2509  {
2510  $this->db=$db;
2511  }
2512 
2519  function fetch($rowid)
2520  {
2521  $sql = 'SELECT ed.rowid, ed.fk_expedition, ed.fk_entrepot, ed.fk_origin_line, ed.qty, ed.rang';
2522  $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as ed';
2523  $sql.= ' WHERE ed.rowid = '.$rowid;
2524  $result = $this->db->query($sql);
2525  if ($result)
2526  {
2527  $objp = $this->db->fetch_object($result);
2528  $this->id = $objp->rowid;
2529  $this->fk_expedition = $objp->fk_expedition;
2530  $this->entrepot_id = $objp->fk_entrepot;
2531  $this->fk_origin_line = $objp->fk_origin_line;
2532  $this->qty = $objp->qty;
2533  $this->rang = $objp->rang;
2534 
2535  $this->db->free($result);
2536 
2537  return 1;
2538  }
2539  else
2540  {
2541  $this->errors[] = $this->db->lasterror();
2542  $this->error = $this->db->lasterror();
2543  return -1;
2544  }
2545  }
2546 
2554  function insert($user=null, $notrigger=0)
2555  {
2556  global $langs, $conf;
2557 
2558  $error=0;
2559 
2560  // Check parameters
2561  if (empty($this->fk_expedition) || empty($this->fk_origin_line) || ! is_numeric($this->qty))
2562  {
2563  $this->error = 'ErrorMandatoryParametersNotProvided';
2564  return -1;
2565  }
2566  // Clean parameters
2567  if (empty($this->entrepot_id)) $this->entrepot_id='null';
2568 
2569  $this->db->begin();
2570 
2571  $sql = "INSERT INTO ".MAIN_DB_PREFIX."expeditiondet (";
2572  $sql.= "fk_expedition";
2573  $sql.= ", fk_entrepot";
2574  $sql.= ", fk_origin_line";
2575  $sql.= ", qty";
2576  $sql.= ") VALUES (";
2577  $sql.= $this->fk_expedition;
2578  $sql.= ", ".$this->entrepot_id;
2579  $sql.= ", ".$this->fk_origin_line;
2580  $sql.= ", ".$this->qty;
2581  $sql.= ")";
2582 
2583  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
2584  $resql = $this->db->query($sql);
2585  if ($resql)
2586  {
2587  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."expeditiondet");
2588 
2589  if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))
2590  {
2591  $result=$this->insertExtraFields();
2592  if ($result < 0)
2593  {
2594  $error++;
2595  }
2596  }
2597 
2598  if (! $error && ! $notrigger)
2599  {
2600  // Call trigger
2601  $result=$this->call_trigger('LINESHIPPING_INSERT',$user);
2602  if ($result < 0)
2603  {
2604  $error++;
2605  }
2606  // End call triggers
2607  }
2608 
2609  if (! $error) {
2610  $this->db->commit();
2611  return $this->id;
2612  }
2613 
2614  foreach($this->errors as $errmsg)
2615  {
2616  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
2617  $this->error.=($this->error?', '.$errmsg:$errmsg);
2618  }
2619 
2620  $this->db->rollback();
2621  return -1*$error;
2622  }
2623  else
2624  {
2625  $error++;
2626  }
2627  }
2628 
2636  function delete($user = null, $notrigger = 0)
2637  {
2638  global $conf;
2639 
2640  $error=0;
2641 
2642  $this->db->begin();
2643 
2644  // delete batch expedition line
2645  if ($conf->productbatch->enabled)
2646  {
2647  $sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
2648  $sql.= " WHERE fk_expeditiondet = ".$this->id;
2649 
2650  if (!$this->db->query($sql))
2651  {
2652  $this->errors[]=$this->db->lasterror()." - sql=$sql";
2653  $error++;
2654  }
2655  }
2656 
2657  $sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet";
2658  $sql.= " WHERE rowid = ".$this->id;
2659 
2660  if (! $error && $this->db->query($sql))
2661  {
2662  // Remove extrafields
2663  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2664  {
2665  $result=$this->deleteExtraFields();
2666  if ($result < 0)
2667  {
2668  $this->errors[]=$this->error;
2669  $error++;
2670  }
2671  }
2672  if (! $error && ! $notrigger)
2673  {
2674  // Call trigger
2675  $result=$this->call_trigger('LINESHIPPING_DELETE',$user);
2676  if ($result < 0)
2677  {
2678  $this->errors[]=$this->error;
2679  $error++;
2680  }
2681  // End call triggers
2682  }
2683  }
2684  else
2685  {
2686  $this->errors[]=$this->db->lasterror()." - sql=$sql";
2687  $error++;
2688  }
2689 
2690  if (! $error) {
2691  $this->db->commit();
2692  return 1;
2693  }
2694  else
2695  {
2696  foreach($this->errors as $errmsg)
2697  {
2698  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
2699  $this->error.=($this->error?', '.$errmsg:$errmsg);
2700  }
2701  $this->db->rollback();
2702  return -1*$error;
2703  }
2704  }
2705 
2713  function update($user = null, $notrigger = 0)
2714  {
2715  global $conf;
2716 
2717  $error=0;
2718 
2719  dol_syslog(get_class($this)."::update id=$this->id, entrepot_id=$this->entrepot_id, product_id=$this->fk_product, qty=$this->qty");
2720 
2721  $this->db->begin();
2722 
2723  // Clean parameters
2724  if (empty($this->qty)) $this->qty=0;
2725  $qty=price2num($this->qty);
2726  $remainingQty = 0;
2727  $batch = null;
2728  $batch_id = null;
2729  $expedition_batch_id = null;
2730  if (is_array($this->detail_batch)) // array of ExpeditionLineBatch
2731  {
2732  if (count($this->detail_batch) > 1)
2733  {
2734  dol_syslog(get_class($this).'::update only possible for one batch', LOG_ERR);
2735  $this->errors[]='ErrorBadParameters';
2736  $error++;
2737  }
2738  else
2739  {
2740  $batch = $this->detail_batch[0]->batch;
2741  $batch_id = $this->detail_batch[0]->fk_origin_stock;
2742  $expedition_batch_id = $this->detail_batch[0]->id;
2743  if ($this->entrepot_id != $this->detail_batch[0]->entrepot_id)
2744  {
2745  dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
2746  $this->errors[]='ErrorBadParameters';
2747  $error++;
2748  }
2749  $qty = price2num($this->detail_batch[0]->qty);
2750  }
2751  }
2752  else if (! empty($this->detail_batch))
2753  {
2754  $batch = $this->detail_batch->batch;
2755  $batch_id = $this->detail_batch->fk_origin_stock;
2756  $expedition_batch_id = $this->detail_batch->id;
2757  if ($this->entrepot_id != $this->detail_batch->entrepot_id)
2758  {
2759  dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
2760  $this->errors[]='ErrorBadParameters';
2761  $error++;
2762  }
2763  $qty = price2num($this->detail_batch->qty);
2764  }
2765 
2766  // check parameters
2767  if (! isset($this->id) || ! isset($this->entrepot_id))
2768  {
2769  dol_syslog(get_class($this).'::update missing line id and/or warehouse id', LOG_ERR);
2770  $this->errors[]='ErrorMandatoryParametersNotProvided';
2771  $error++;
2772  return -1;
2773  }
2774 
2775  // update lot
2776 
2777  if (! empty($batch) && $conf->productbatch->enabled)
2778  {
2779  dol_syslog(get_class($this)."::update expedition batch id=$expedition_batch_id, batch_id=$batch_id, batch=$batch");
2780 
2781  if (empty($batch_id) || empty($this->fk_product)) {
2782  dol_syslog(get_class($this).'::update missing fk_origin_stock (batch_id) and/or fk_product', LOG_ERR);
2783  $this->errors[]='ErrorMandatoryParametersNotProvided';
2784  $error++;
2785  }
2786 
2787  // fetch remaining lot qty
2788  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
2789  if (! $error && ($lotArray = ExpeditionLineBatch::fetchAll($this->db, $this->id)) < 0)
2790  {
2791  $this->errors[]=$this->db->lasterror()." - ExpeditionLineBatch::fetchAll";
2792  $error++;
2793  }
2794  else
2795  {
2796  // caculate new total line qty
2797  foreach ($lotArray as $lot)
2798  {
2799  if ($expedition_batch_id != $lot->id)
2800  {
2801  $remainingQty += $lot->qty;
2802  }
2803  }
2804  $qty += $remainingQty;
2805 
2806  //fetch lot details
2807 
2808  // fetch from product_lot
2809  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
2810  $lot = new Productlot($this->db);
2811  if ($lot->fetch(0,$this->fk_product,$batch) < 0)
2812  {
2813  $this->errors[] = $lot->errors;
2814  $error++;
2815  }
2816  if (! $error && ! empty($expedition_batch_id))
2817  {
2818  // delete lot expedition line
2819  $sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
2820  $sql.= " WHERE fk_expeditiondet = ".$this->id;
2821  $sql.= " AND rowid = ".$expedition_batch_id;
2822 
2823  if (!$this->db->query($sql))
2824  {
2825  $this->errors[]=$this->db->lasterror()." - sql=$sql";
2826  $error++;
2827  }
2828  }
2829  if (! $error && $this->detail_batch->qty > 0)
2830  {
2831  // create lot expedition line
2832  if (isset($lot->id))
2833  {
2834  $shipmentLot = new ExpeditionLineBatch($this->db);
2835  $shipmentLot->batch = $lot->batch;
2836  $shipmentLot->eatby = $lot->eatby;
2837  $shipmentLot->sellby = $lot->sellby;
2838  $shipmentLot->entrepot_id = $this->detail_batch->entrepot_id;
2839  $shipmentLot->qty = $this->detail_batch->qty;
2840  $shipmentLot->fk_origin_stock = $batch_id;
2841  if ($shipmentLot->create($this->id) < 0)
2842  {
2843  $this->errors[]=$shipmentLot->errors;
2844  $error++;
2845  }
2846  }
2847  }
2848  }
2849  }
2850  if (! $error)
2851  {
2852  // update line
2853  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
2854  $sql.= " fk_entrepot = ".($this->entrepot_id > 0 ? $this->entrepot_id : 'null');
2855  $sql.= " , qty = ".$qty;
2856  $sql.= " WHERE rowid = ".$this->id;
2857 
2858  if (!$this->db->query($sql))
2859  {
2860  $this->errors[]=$this->db->lasterror()." - sql=$sql";
2861  $error++;
2862  }
2863  }
2864 
2865  if (! $error)
2866  {
2867  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2868  {
2869  $result=$this->insertExtraFields();
2870  if ($result < 0)
2871  {
2872  $this->errors[]=$this->error;
2873  $error++;
2874  }
2875  }
2876  }
2877 
2878  if (! $error && ! $notrigger)
2879  {
2880  // Call trigger
2881  $result=$this->call_trigger('LINESHIPPING_UPDATE',$user);
2882  if ($result < 0)
2883  {
2884  $this->errors[]=$this->error;
2885  $error++;
2886  }
2887  // End call triggers
2888  }
2889  if (!$error) {
2890  $this->db->commit();
2891  return 1;
2892  }
2893  else
2894  {
2895  foreach($this->errors as $errmsg)
2896  {
2897  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2898  $this->error.=($this->error?', '.$errmsg:$errmsg);
2899  }
2900  $this->db->rollback();
2901  return -1*$error;
2902  }
2903  }
2904 }
update($user=null, $notrigger=0)
Update database.
update_delivery_method($id='')
Update/create delivery method.
Class to manage stock movements.
addline_batch($dbatch, $array_options=0)
Add a shipment line with batch record.
fetch_delivery_methods()
Fetch deliveries method and return an array.
__construct($db)
Constructor.
reOpen()
Classify the shipping as validated/opened.
Class with list of lots and properties.
print
Draft customers invoices.
Definition: index.php:91
Classe de gestion des lignes de bons d&#39;expedition.
if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) if(! empty($conf->don->enabled) && $user->rights->societe->lire) if(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) if(! empty($conf->facture->enabled) &&! empty($conf->commande->enabled) && $user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1053
disable_delivery_method($id)
DesActivate delivery method.
insert($user=null, $notrigger=0)
Insert line into database.
Class to manage receptions.
CRUD class for batch number management within shipment.
list_delivery_methods($id='')
Fetch all deliveries method and return an array.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
Delete all links between an object $this.
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). ...
const STATUS_DRAFT
Draft status.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
if(! empty($search_group)) natural_search(array("g.nom" g note
Definition: list.php:123
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
Class to manage products or services.
create_line($entrepot_id, $origin_line_id, $qty, $array_options=0)
Create a expedition line.
Class to manage Dolibarr database access.
create_line_batch($line_ext, $array_options=0)
Create the detail (eat-by date) of the expedition line.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_origin()
Read linked origin object.
addline($entrepot_id, $id, $qty, $array_options=0)
Add an expedition line.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
static fetchAll($db, $id_line_expdet, $fk_product=0)
Retrieve all batch number detailed information of a shipment line.
const STATUS_SHIPMENTONPROCESS
Shipment on process.
set_date_livraison($user, $date_livraison)
Set the planned delivery date.
deleteline($user, $lineid)
Delete detail line.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
set_billed()
Classify the shipping as invoiced (used when WORKFLOW_BILL_ON_SHIPMENT is on)
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller Note: This function applies same rules than get_default_tva.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
update($user=null, $notrigger=0)
Update a line in database.
const STATUS_CLOSED
Closed status.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage order lines.
fetch_lines()
Load lines.
fetch($rowid)
Load line expedition.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
Class to manage third parties objects (customers, suppliers, prospects...)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
valid($user, $notrigger=0)
Validate object and update stock if option enabled.
initAsSpecimen()
Initialise an instance with random values.
Class to manage shipments.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
setStatut($status, $elementId=null, $elementType='', $trigkey='')
Set status of an object.
Class to manage customers orders.
create($user, $notrigger=0)
Create expedition en base.
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
Return clicable link of object (with eventually picto)
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1273
$conf db user
Definition: repair.php:104
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1)
Remove a file or several files with a mask.
Definition: files.lib.php:1139
__construct($db)
Constructor.
deleteExtraFields()
Delete all extra fields values for the current object.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:59
create_delivery($user)
Create a delivery receipt from a shipment.
dol_now($mode='gmt')
Return date for now.
GetUrlTrackingStatus($value='')
Forge an set tracking url.
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...
const STATUS_VALIDATED
Validated status.
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)
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:85
LibStatut($statut, $mode)
Return label of a status.
const STATUS_VALIDATED
Validated status.
fetch($id, $ref='', $ref_ext='', $ref_int='')
Get object and lines from database.
Manage record for batch number management.
getNextNumRef($soc)
Return next contract ref.
getLibStatut($mode=0)
Return status label.
static deletefromexp($db, $id_expedition)
Delete batch record attach to a shipment.
call_trigger($trigger_name, $user)
Call trigger based on this instance.
activ_delivery_method($id)
Activate delivery method.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it&#39;s its name (generic function)
price2num($amount, $rounding='', $alreadysqlnb=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setClosed()
Classify the shipping as closed.