dolibarr 25.0.0-alpha
delivery.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2006-2007 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
6 * Copyright (C) 2011-2023 Philippe Grand <philippe.grand@atoo-net.com>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
9 * Copyright (C) 2023-2026 Frédéric France <frederic.france@free.fr>
10 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
32require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
33require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
35require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
36if (isModEnabled("propal")) {
37 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
38}
39if (isModEnabled('order')) {
40 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
41}
42
43
48{
49 use CommonIncoterm;
50
54 public $element = "delivery";
55
60 public $TRIGGER_PREFIX = 'DELIVERY';
61
65 public $fk_element = "fk_delivery";
66
70 public $table_element = "delivery";
71
75 public $table_element_line = "deliverydet";
76
80 public $picto = 'sending';
81
85 public $draft;
86
90 public $socid;
91
95 public $ref_customer;
96
100 public $date_delivery;
101
105 public $date_valid;
106
110 public $model_pdf;
111
115 public $commande_id;
116
120 public $lines = array();
121
125 public $user_author_id;
126
127
128 const STATUS_DRAFT = 0;
129 const STATUS_VALIDATED = 1;
130 const STATUS_CANCELED = -1;
131
132
138 public function __construct($db)
139 {
140 $this->db = $db;
141
142 // List of short language codes for status
143 $this->labelStatus[-1] = 'StatusDeliveryCanceled';
144 $this->labelStatus[0] = 'StatusDeliveryDraft';
145 $this->labelStatus[1] = 'StatusDeliveryValidated';
146 }
147
154 public function create($user)
155 {
156 global $conf;
157
158 dol_syslog("Delivery::create");
159
160 if (empty($this->model_pdf)) {
161 $this->model_pdf = getDolGlobalString('DELIVERY_ADDON_PDF');
162 }
163
164 $error = 0;
165 $now = dol_now();
166 $this->ref_customer = trim((string) $this->ref_customer);
167
168 /* Delivery note as draft On positionne en mode draft le bon de livraison */
169 $this->draft = 1;
170
171 $this->user = $user;
172
173 $this->db->begin();
174
175 $sql = "INSERT INTO ".MAIN_DB_PREFIX."delivery (";
176 $sql .= "ref";
177 $sql .= ", entity";
178 $sql .= ", fk_soc";
179 $sql .= ", ref_customer";
180 $sql .= ", date_creation";
181 $sql .= ", fk_user_author";
182 $sql .= ", date_delivery";
183 $sql .= ", fk_address";
184 $sql .= ", note_private";
185 $sql .= ", note_public";
186 $sql .= ", model_pdf";
187 $sql .= ", fk_incoterms, location_incoterms";
188 $sql .= ") VALUES (";
189 $sql .= "'(PROV)'";
190 $sql .= ", ".((int) $conf->entity);
191 $sql .= ", ".((int) $this->socid);
192 $sql .= ", '".$this->db->escape($this->ref_customer)."'";
193 $sql .= ", '".$this->db->idate($now)."'";
194 $sql .= ", ".((int) $user->id);
195 $sql .= ", ".($this->date_delivery ? "'".$this->db->idate($this->date_delivery)."'" : "null");
196 $sql .= ", ".($this->fk_delivery_address > 0 ? ((int) $this->fk_delivery_address) : "null");
197 $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null");
198 $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null");
199 $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null");
200 $sql .= ", ".(int) $this->fk_incoterms;
201 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
202 $sql .= ")";
203
204 dol_syslog("Delivery::create", LOG_DEBUG);
205
206 $resql = $this->db->query($sql);
207 if ($resql) {
208 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."delivery");
209
210 $numref = "(PROV".$this->id.")";
211
212 $sql = "UPDATE ".MAIN_DB_PREFIX."delivery ";
213 $sql .= "SET ref = '".$this->db->escape($numref)."'";
214 $sql .= " WHERE rowid = ".((int) $this->id);
215
216 dol_syslog("Delivery::create", LOG_DEBUG);
217 $resql = $this->db->query($sql);
218 if ($resql) {
219 if (!getDolGlobalInt('MAIN_SUBMODULE_EXPEDITION')) {
220 $commande = new Commande($this->db);
221 $commande->id = $this->commande_id;
222 $commande->fetch_lines();
223 }
224
225
226 /*
227 * Inserting products into the database
228 */
229 $num = count($this->lines);
230 for ($i = 0; $i < $num; $i++) {
231 $origin_id = $this->lines[$i]->origin_line_id;
232 if (!$origin_id) {
233 $origin_id = $this->lines[$i]->commande_ligne_id; // For backward compatibility
234 }
235
236 if (!$this->create_line($origin_id, $this->lines[$i]->qty, $this->lines[$i]->fk_product, $this->lines[$i]->description, $this->lines[$i]->array_options)) {
237 $error++;
238 }
239 }
240
241 if (!$error && $this->id && $this->origin_id) {
242 $ret = $this->add_object_linked();
243 if (!$ret) {
244 $error++;
245 }
246
247 if (!getDolGlobalInt('MAIN_SUBMODULE_EXPEDITION')) {
248 $ret = $this->setStatut(2, $this->origin_id, $this->origin);
249 if (!$ret) {
250 $error++;
251 }
252 }
253 }
254
255 if (!$error) {
256 // Call trigger
257 $result = $this->call_trigger('DELIVERY_CREATE', $user);
258 if ($result < 0) {
259 $error++;
260 }
261 // End call triggers
262 }
263
264 if (!$error) {
265 $this->db->commit();
266 return $this->id;
267 } else {
268 $error++;
269 $this->error = $this->db->lasterror()." - sql=".$this->db->lastqueryerror;
270 $this->db->rollback();
271 return -3;
272 }
273 } else {
274 $error++;
275 $this->error = $this->db->lasterror()." - sql=".$this->db->lastqueryerror;
276 $this->db->rollback();
277 return -2;
278 }
279 } else {
280 $error++;
281 $this->error = $this->db->lasterror()." - sql=".$this->db->lastqueryerror;
282 $this->db->rollback();
283 return -1;
284 }
285 }
286
287 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
298 public function create_line($origin_id, $qty, $fk_product, $description, $array_options = [])
299 {
300 // phpcs:enable
301 $error = 0;
302
303 $sql = "INSERT INTO ".MAIN_DB_PREFIX."deliverydet (fk_delivery, fk_origin_line,";
304 $sql .= " fk_product, description, qty)";
305 $sql .= " VALUES (".((int) $this->id).",".((int) $origin_id).",";
306 $sql .= " ".($fk_product > 0 ? ((int) $fk_product) : "null").",";
307 $sql .= " ".($description ? "'".$this->db->escape($description)."'" : "null").",";
308 $sql .= (price2num($qty, 'MS')).")";
309
310 dol_syslog(get_class($this)."::create_line", LOG_DEBUG);
311 if (!$this->db->query($sql)) {
312 $error++;
313 }
314
315 $id = $this->db->last_insert_id(MAIN_DB_PREFIX."deliverydet");
316
317 if (is_array($array_options) && count($array_options) > 0) {
318 $line = new DeliveryLine($this->db);
319 $line->id = $id;
320 $line->array_options = $array_options;
321 $result = $line->insertExtraFields();
322 }
323
324 if (!$error) {
325 return 1;
326 }
327
328 return -1;
329 }
330
337 public function fetch($id)
338 {
339 $sql = "SELECT l.rowid, l.fk_soc, l.date_creation, l.date_valid, l.ref, l.ref_customer, l.fk_user_author,";
340 $sql .= " l.total_ht, l.fk_statut, l.fk_user_valid, l.note_private, l.note_public";
341 $sql .= ", l.date_delivery, l.fk_address, l.model_pdf";
342 $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
343 $sql .= ', l.fk_incoterms, l.location_incoterms';
344 $sql .= ", i.libelle as label_incoterms";
345 $sql .= " FROM ".MAIN_DB_PREFIX."delivery as l";
346 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = l.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
347 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON l.fk_incoterms = i.rowid';
348 $sql .= " WHERE l.rowid = ".((int) $id);
349
350 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
351 $result = $this->db->query($sql);
352 if ($result) {
353 if ($this->db->num_rows($result)) {
354 $obj = $this->db->fetch_object($result);
355
356 $this->id = $obj->rowid;
357 $this->date_delivery = $this->db->jdate($obj->date_delivery);
358 $this->date_creation = $this->db->jdate($obj->date_creation);
359 $this->date_valid = $this->db->jdate($obj->date_valid);
360 $this->ref = $obj->ref;
361 $this->ref_customer = $obj->ref_customer;
362 $this->socid = $obj->fk_soc;
363 $this->statut = $obj->fk_statut;
364 $this->status = $obj->fk_statut;
365 $this->user_author_id = $obj->fk_user_author;
366 $this->user_validation_id = $obj->fk_user_valid;
367 $this->fk_delivery_address = $obj->fk_address;
368 $this->note = $obj->note_private; //TODO deprecated
369 $this->note_private = $obj->note_private;
370 $this->note_public = $obj->note_public;
371 $this->model_pdf = $obj->model_pdf;
372 $this->origin = $obj->origin; // May be 'shipping'
373 $this->origin_type = $obj->origin; // May be 'shipping'
374 $this->origin_id = $obj->origin_id; // May be id of shipping
375
376 //Incoterms
377 $this->fk_incoterms = $obj->fk_incoterms;
378 $this->location_incoterms = $obj->location_incoterms;
379 $this->label_incoterms = $obj->label_incoterms;
380 $this->db->free($result);
381
382 if ($this->status == 0) {
383 $this->draft = 1;
384 }
385
386 // Retrieve all extrafields
387 // fetch optionals attributes and labels
388 $this->fetch_optionals();
389
390 // Load lines
391 $result = $this->fetch_lines();
392 if ($result < 0) {
393 return -3;
394 }
395
396 return 1;
397 } else {
398 $this->error = 'Delivery with id '.$id.' not found sql='.$sql;
399 dol_syslog(get_class($this).'::fetch Error '.$this->error, LOG_ERR);
400 return -2;
401 }
402 } else {
403 $this->error = $this->db->error();
404 return -1;
405 }
406 }
407
415 public function valid($user, $notrigger = 0)
416 {
417 global $conf;
418
419 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
420
421 dol_syslog(get_class($this)."::valid begin");
422
423 if (!isset($this->socid)) {
424 dol_syslog(get_class($this)."::can't valid socid not set", LOG_WARNING);
425 return 0;
426 }
427
428 $this->db->begin();
429
430 $error = 0;
431
432 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'delivery', 'creer'))
433 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'delivery_advance', 'validate'))) {
434 if (getDolGlobalString('DELIVERY_ADDON_NUMBER')) {
435 // Setting the command numbering module name
436 $modName = getDolGlobalString('DELIVERY_ADDON_NUMBER');
437
438 if (is_readable(DOL_DOCUMENT_ROOT.'/core/modules/delivery/'.$modName.'.php')) {
439 require_once DOL_DOCUMENT_ROOT.'/core/modules/delivery/'.$modName.'.php';
440
441 $now = dol_now();
442
443 // Retrieving the new reference
444 $objMod = new $modName($this->db);
445 '@phan-var-force ModeleNumRefDeliveryOrder $objMod';
447 $soc = new Societe($this->db);
448 $soc->fetch($this->socid);
449
450 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
451 $numref = $objMod->getNextValue($soc, $this);
452 } else {
453 $numref = (string) $this->ref;
454 }
455 $this->newref = dol_sanitizeFileName($numref);
456
457 // Test if is not already in valid status. If so, we stop to avoid decrementing the stock twice.
458 $sql = "SELECT ref";
459 $sql .= " FROM ".MAIN_DB_PREFIX."delivery";
460 $sql .= " WHERE ref = '".$this->db->escape($numref)."'";
461 $sql .= " AND fk_statut <> 0";
462 $sql .= " AND entity = ".((int) $conf->entity);
463
464 $resql = $this->db->query($sql);
465 if ($resql) {
466 $num = $this->db->num_rows($resql);
467 if ($num > 0) {
468 return 0;
469 }
470 }
471
472 $sql = "UPDATE ".MAIN_DB_PREFIX."delivery SET";
473 $sql .= " ref = '".$this->db->escape($numref)."'";
474 $sql .= ", fk_statut = 1";
475 $sql .= ", date_valid = '".$this->db->idate($now)."'";
476 if (!empty($this->date_delivery)) {
477 $sql .= ", date_delivery = '".$this->db->idate($this->date_delivery)."'";
478 }
479 $sql .= ", fk_user_valid = ".((int) $user->id);
480 $sql .= " WHERE rowid = ".((int) $this->id);
481 $sql .= " AND fk_statut = 0";
482
483 $resql = $this->db->query($sql);
484 if (!$resql) {
485 dol_print_error($this->db);
486 $this->error = $this->db->lasterror();
487 $error++;
488 }
489
490 if (!$error && !$notrigger) {
491 // Call trigger
492 $result = $this->call_trigger('DELIVERY_VALIDATE', $user);
493 if ($result < 0) {
494 $error++;
495 }
496 // End call triggers
497 }
498
499 if (!$error) {
500 $this->oldref = $this->ref;
501
502 // Rename directory if dir was a temporary ref
503 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
504 // Now we rename also files into index
505 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'expedition/receipt/".$this->db->escape($this->newref)."'";
506 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expedition/receipt/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
507 $resql = $this->db->query($sql);
508 if (!$resql) {
509 $error++;
510 $this->error = $this->db->lasterror();
511 }
512 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'expedition/receipt/".$this->db->escape($this->newref)."'";
513 $sql .= " WHERE filepath = 'expedition/receipt/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
514 $resql = $this->db->query($sql);
515 if (!$resql) {
516 $error++;
517 $this->error = $this->db->lasterror();
518 }
519
520 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
521 $oldref = dol_sanitizeFileName($this->ref);
522 $newref = dol_sanitizeFileName($numref);
523 $dirsource = $conf->expedition->dir_output.'/receipt/'.$oldref;
524 $dirdest = $conf->expedition->dir_output.'/receipt/'.$newref;
525 if (!$error && file_exists($dirsource)) {
526 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
527
528 if (@rename($dirsource, $dirdest)) {
529 dol_syslog("Rename ok");
530 // Rename docs starting with $oldref with $newref
531 $listoffiles = dol_dir_list($conf->expedition->dir_output.'/receipt/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
532 foreach ($listoffiles as $fileentry) {
533 $dirsource = $fileentry['name'];
534 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
535 $dirsource = $fileentry['path'].'/'.$dirsource;
536 $dirdest = $fileentry['path'].'/'.$dirdest;
537 @rename($dirsource, $dirdest);
538 }
539 }
540 }
541 }
542
543 // Set new ref and current status
544 if (!$error) {
545 $this->ref = $numref;
546 $this->status = 1;
547 }
548
549 dol_syslog(get_class($this)."::valid ok");
550 }
551
552 if (!$error) {
553 $this->db->commit();
554 return 1;
555 } else {
556 $this->db->rollback();
557 return -1;
558 }
559 }
560 }
561
562 return -1;
563 } else {
564 $this->error = "NotAllowed";
565 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
566 return -1;
567 }
568 }
569
570 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
578 public function create_from_sending($user, $sending_id)
579 {
580 // phpcs:enable
581 global $conf;
582
583 $expedition = new Expedition($this->db);
584 $result = $expedition->fetch($sending_id);
585 if ($result <= 0) {
586 return $result;
587 }
588
589 $this->lines = array();
590
591 $num = count($expedition->lines);
592 for ($i = 0; $i < $num; $i++) {
593 $line = new DeliveryLine($this->db);
594 $line->origin_line_id = $expedition->lines[$i]->origin_line_id;
595 $line->label = $expedition->lines[$i]->label;
596 $line->description = $expedition->lines[$i]->description;
597 $line->qty = $expedition->lines[$i]->qty_shipped;
598 $line->fk_product = $expedition->lines[$i]->fk_product;
599 if (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED') && is_array($expedition->lines[$i]->array_options) && count($expedition->lines[$i]->array_options) > 0) { // For avoid conflicts if trigger used
600 $line->array_options = $expedition->lines[$i]->array_options;
601 }
602 $this->lines[$i] = $line;
603 }
604
605 $this->origin = $expedition->element;
606 $this->origin_id = $expedition->id;
607 $this->note_private = $expedition->note_private;
608 $this->note_public = $expedition->note_public;
609 $this->fk_project = $expedition->fk_project;
610 $this->date_delivery = ''; // Date of real reception. The Expedition->date_delivery is the planned one.
611 $this->fk_delivery_address = $expedition->fk_delivery_address;
612 $this->socid = $expedition->socid;
613 $this->ref_customer = $expedition->ref_customer;
614
615 //Incoterms
616 $this->fk_incoterms = $expedition->fk_incoterms;
617 $this->location_incoterms = $expedition->location_incoterms;
618
619 return $this->create($user);
620 }
621
622 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
630 public function update_line($id, $array_options = [])
631 {
632 // phpcs:enable
633 global $conf;
634 $error = 0;
635
636 if ($id > 0 && !$error && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) { // For avoid conflicts if trigger used
637 $line = new DeliveryLine($this->db);
638 $line->array_options = $array_options;
639 $line->id = $id;
640 $result = $line->insertExtraFields();
641
642 if ($result < 0) {
643 $this->errors[] = $line->error;
644 $error++;
645 }
646 }
647
648 if (!$error) {
649 return 1;
650 } else {
651 return -1;
652 }
653 }
654
655
664 public function addline($origin_id, $qty, $array_options = [])
665 {
666 $num = count($this->lines);
667 $line = new DeliveryLine($this->db);
668
669 $line->origin_id = $origin_id;
670 $line->qty = $qty;
671 if (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) { // For avoid conflicts if trigger used
672 $line->array_options = $array_options;
673 }
674 $this->lines[$num] = $line;
675 }
676
683 public function deleteLine($lineid)
684 {
685 if ($this->status == 0) {
686 $sql = "DELETE FROM ".MAIN_DB_PREFIX."commandedet";
687 $sql .= " WHERE rowid = ".((int) $lineid);
688
689 if ($this->db->query($sql)) {
690 $this->update_price(1);
691
692 return 1;
693 } else {
694 return -1;
695 }
696 }
697
698 return 0;
699 }
700
707 public function delete($user = null)
708 {
709 global $conf, $langs;
710
711 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
712
713 $this->db->begin();
714
715 $error = 0;
716
717 $sql = "DELETE FROM ".MAIN_DB_PREFIX."deliverydet";
718 $sql .= " WHERE fk_delivery = ".((int) $this->id);
719 if ($this->db->query($sql)) {
720 // Delete linked object
721 $res = $this->deleteObjectLinked();
722 if ($res < 0) {
723 $error++;
724 }
725
726 if (!$error) {
727 $sql = "DELETE FROM ".MAIN_DB_PREFIX."delivery";
728 $sql .= " WHERE rowid = ".((int) $this->id);
729 if ($this->db->query($sql)) {
730 $this->db->commit();
731
732 // Deleting pdf folder's draft On efface le repertoire de pdf provisoire
733 $ref = dol_sanitizeFileName($this->ref);
734 if (!empty($conf->expedition->dir_output)) {
735 $dir = $conf->expedition->dir_output.'/receipt/'.$ref;
736 $file = $dir.'/'.$ref.'.pdf';
737 if (file_exists($file)) {
738 if (!dol_delete_file($file)) {
739 return 0;
740 }
741 }
742 if (file_exists($dir)) {
743 if (!dol_delete_dir($dir)) {
744 $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
745 return 0;
746 }
747 }
748 }
749
750 // Call trigger
751 $result = $this->call_trigger('DELIVERY_DELETE', $user);
752 if ($result < 0) {
753 $this->db->rollback();
754 return -4;
755 }
756 // End call triggers
757
758 return 1;
759 } else {
760 $this->error = $this->db->lasterror()." - sql=$sql";
761 $this->db->rollback();
762 return -3;
763 }
764 } else {
765 $this->error = $this->db->lasterror()." - sql=$sql";
766 $this->db->rollback();
767 return -2;
768 }
769 } else {
770 $this->error = $this->db->lasterror()." - sql=$sql";
771 $this->db->rollback();
772 return -1;
773 }
774 }
775
782 public function getTooltipContentArray($params)
783 {
784 global $langs;
785
786 $langs->load('sendings');
787
788 $datas = [];
789
790 $datas['picto'] = img_picto('', $this->picto, '', 0, 0, 0, '', 'paddingrightonly').' <u>'.$langs->trans("ShowReceiving").'</u>:<br>';
791 $datas['ref'] = '<b>'.$langs->trans("Ref").'</b>: '.$this->ref;
792
793 return $datas;
794 }
795
803 public function getNomUrl($withpicto = 0, $save_lastsearch_value = -1)
804 {
805 global $langs, $hookmanager;
806
807 $result = '';
808
809 $params = [
810 'id' => $this->id,
811 'objecttype' => $this->element,
812 ];
813 $classfortooltip = 'classfortooltip';
814 $dataparams = '';
815 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
816 $classfortooltip = 'classforajaxtooltip';
817 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
818 $label = '';
819 } else {
820 $label = implode($this->getTooltipContentArray($params));
821 }
822
823 $url = DOL_URL_ROOT.'/delivery/card.php?id='.$this->id;
824
825 //if ($option !== 'nolink')
826 //{
827 // Add param to save lastsearch_values or not
828 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
829 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
830 $add_save_lastsearch_values = 1;
831 }
832 if ($add_save_lastsearch_values) {
833 $url .= '&save_lastsearch_values=1';
834 }
835 //}
836
837 $linkstart = '<a href="'.$url.'"';
838 $linkstart .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
839 $linkstart .= $dataparams.' class="'.$classfortooltip.'">';
840 $linkend = '</a>';
841
842 if ($withpicto) {
843 $result .= ($linkstart.img_object($label, $this->picto, $dataparams.' class="'.$classfortooltip.'"').$linkend);
844 }
845 if ($withpicto && $withpicto != 2) {
846 $result .= ' ';
847 }
848 $result .= $linkstart.$this->ref.$linkend;
849
850 global $action;
851 $hookmanager->initHooks(array($this->element . 'dao'));
852 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
853 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
854 if ($reshook > 0) {
855 $result = $hookmanager->resPrint;
856 } else {
857 $result .= $hookmanager->resPrint;
858 }
859 return $result;
860 }
861
862 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
868 public function fetch_lines()
869 {
870 // phpcs:enable
871 $this->lines = array();
872
873 $sql = "SELECT ld.rowid, ld.fk_product, ld.description, ld.subprice, ld.total_ht, ld.qty as qty_shipped, ld.fk_origin_line, ld.extraparams,";
874 $sql .= " cd.qty as qty_asked, cd.label as custom_label, cd.fk_unit,";
875 $sql .= " p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc,";
876 $sql .= " p.weight, p.weight_units, p.width, p.width_units, p.length, p.length_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tobatch as product_tobatch";
877 $sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd, ".MAIN_DB_PREFIX."deliverydet as ld";
878 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p on p.rowid = ld.fk_product";
879 $sql .= " WHERE ld.fk_origin_line = cd.rowid";
880 $sql .= " AND ld.fk_delivery = ".((int) $this->id);
881
882 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
883 $resql = $this->db->query($sql);
884 if ($resql) {
885 $num = $this->db->num_rows($resql);
886 $i = 0;
887 while ($i < $num) {
888 $obj = $this->db->fetch_object($resql);
889
890 $line = new DeliveryLine($this->db);
891
892 $line->id = $obj->rowid;
893 $line->label = $obj->custom_label;
894 $line->description = $obj->description;
895 $line->fk_product = $obj->fk_product;
896 $line->qty_asked = $obj->qty_asked;
897 $line->qty_shipped = $obj->qty_shipped;
898
899 $line->product_label = $obj->product_label; // Product label
900 $line->product_ref = $obj->product_ref; // Product ref
901 $line->product_desc = $obj->product_desc; // Product description
902 $line->product_type = $obj->fk_product_type;
903
904 $line->fk_origin_line = $obj->fk_origin_line;
905
906 $line->price = $obj->subprice;
907 $line->total_ht = $obj->total_ht;
908
909 // units
910 $line->weight = $obj->weight;
911 $line->weight_units = $obj->weight_units;
912 $line->width = $obj->width;
913 $line->width_units = $obj->width_units;
914 $line->height = $obj->height;
915 $line->height_units = $obj->height_units;
916 $line->length = $obj->length;
917 $line->length_units = $obj->length_units;
918 $line->surface = $obj->surface;
919 $line->surface_units = $obj->surface_units;
920 $line->volume = $obj->volume;
921 $line->volume_units = $obj->volume_units;
922
923 $line->fk_unit = $obj->fk_unit;
924 $line->fetch_optionals();
925
926 $line->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
927
928 $this->lines[$i] = $line;
929
930 $i++;
931 }
932 $this->db->free($resql);
933
934 return 1;
935 } else {
936 return -1;
937 }
938 }
939
940
947 public function getLibStatut($mode = 0)
948 {
949 return $this->LibStatut($this->status, $mode);
950 }
951
952 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
960 public function LibStatut($status, $mode)
961 {
962 // phpcs:enable
963 global $langs;
964
965 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
966 global $langs;
967 //$langs->load("mymodule");
968 $this->labelStatus[-1] = $langs->transnoentitiesnoconv('StatusDeliveryCanceled');
969 $this->labelStatus[0] = $langs->transnoentitiesnoconv('StatusDeliveryDraft');
970 $this->labelStatus[1] = $langs->transnoentitiesnoconv('StatusDeliveryValidated');
971 $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv('StatusDeliveryCanceled');
972 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv('StatusDeliveryDraft');
973 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv('StatusDeliveryValidated');
974 }
975
976 $statusType = 'status0';
977 if ($status == -1) {
978 $statusType = 'status5';
979 }
980 if ($status == 1) {
981 $statusType = 'status4';
982 }
983
984 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
985 }
986
987
995 public function initAsSpecimen()
996 {
997 $now = dol_now();
998
999 // Load array of products prodids
1000 $num_prods = 0;
1001 $prodids = array();
1002 $sql = "SELECT rowid";
1003 $sql .= " FROM ".MAIN_DB_PREFIX."product";
1004 $sql .= " WHERE entity IN (".getEntity('product').")";
1005 $sql .= " AND tosell = 1";
1006 $sql .= $this->db->plimit(100);
1007
1008 $resql = $this->db->query($sql);
1009 if ($resql) {
1010 $num_prods = $this->db->num_rows($resql);
1011 $i = 0;
1012 while ($i < $num_prods) {
1013 $i++;
1014 $row = $this->db->fetch_row($resql);
1015 $prodids[$i] = $row[0];
1016 }
1017 }
1018
1019 // Initialise parameters
1020 $this->id = 0;
1021 $this->ref = 'SPECIMEN';
1022 $this->specimen = 1;
1023 $this->socid = 1;
1024 $this->date_delivery = $now;
1025 $this->note_public = 'Public note';
1026 $this->note_private = 'Private note';
1027
1028 $i = 0;
1029 $line = new DeliveryLine($this->db);
1030 $line->fk_product = reset($prodids);
1031 $line->qty_asked = 10;
1032 $line->qty_shipped = 9;
1033 $line->product_ref = 'REFPROD';
1034 $line->label = 'Specimen';
1035 $line->description = 'Description';
1036 $line->price = 100;
1037 $line->total_ht = 100;
1038
1039 $this->lines[$i] = $line;
1040
1041 return 1;
1042 }
1043
1050 public function getRemainingDelivered()
1051 {
1052 // Get the linked object
1053 $this->fetchObjectLinked(null, '', $this->id, $this->element);
1054 //var_dump($this->linkedObjectsIds);
1055 // Get the product ref and qty in source
1056 $sqlSourceLine = "SELECT st.rowid, st.description, st.qty";
1057 $sqlSourceLine .= ", p.ref, p.label";
1058 $sqlSourceLine .= " FROM ".MAIN_DB_PREFIX.$this->db->sanitize($this->linkedObjectsIds[0]['type'])."det as st";
1059 $sqlSourceLine .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON st.fk_product = p.rowid";
1060 $sqlSourceLine .= " WHERE fk_".$this->db->sanitize($this->linked_objects[0]['type'])." = ".((int) $this->linked_objects[0]['linkid']);
1061
1062 $resultSourceLine = $this->db->query($sqlSourceLine);
1063 if ($resultSourceLine) {
1064 $num_lines = $this->db->num_rows($resultSourceLine);
1065 $i = 0;
1066 $array = array();
1067 while ($i < $num_lines) {
1068 $objSourceLine = $this->db->fetch_object($resultSourceLine);
1069
1070 // Get lines of sources already delivered
1071 $sql = "SELECT ld.fk_origin_line, sum(ld.qty) as qty";
1072 $sql .= " FROM ".MAIN_DB_PREFIX."deliverydet as ld, ".MAIN_DB_PREFIX."delivery as l,";
1073 $sql .= " ".MAIN_DB_PREFIX.$this->db->sanitize($this->linked_objects[0]['type'])." as c";
1074 $sql .= ", ".MAIN_DB_PREFIX.$this->db->sanitize($this->linked_objects[0]['type'])."det as cd";
1075 $sql .= " WHERE ld.fk_delivery = l.rowid";
1076 $sql .= " AND ld.fk_origin_line = cd.rowid";
1077 $sql .= " AND cd.fk_".$this->db->sanitize($this->linked_objects[0]['type'])." = c.rowid";
1078 $sql .= " AND cd.fk_".$this->db->sanitize($this->linked_objects[0]['type'])." = ".((int) $this->linked_objects[0]['linkid']);
1079 $sql .= " AND ld.fk_origin_line = ".((int) $objSourceLine->rowid);
1080 $sql .= " GROUP BY ld.fk_origin_line";
1081
1082 $result = $this->db->query($sql);
1083 $row = $this->db->fetch_row($result);
1084
1085 if ($objSourceLine->qty - $row[1] > 0) {
1086 if ($row[0] == $objSourceLine->rowid) {
1087 $array[$i]['qty'] = $objSourceLine->qty - $row[1];
1088 } else {
1089 $array[$i]['qty'] = $objSourceLine->qty;
1090 }
1091
1092 $array[$i]['ref'] = $objSourceLine->ref;
1093 $array[$i]['label'] = $objSourceLine->label ? $objSourceLine->label : $objSourceLine->description;
1094 } elseif ($objSourceLine->qty - $row[1] < 0) {
1095 $array[$i]['qty'] = $objSourceLine->qty - $row[1]." Erreur livraison !";
1096 $array[$i]['ref'] = $objSourceLine->ref;
1097 $array[$i]['label'] = $objSourceLine->label ? $objSourceLine->label : $objSourceLine->description;
1098 }
1099
1100 $i++;
1101 }
1102 return $array;
1103 } else {
1104 $this->error = $this->db->error()." - sql=$sqlSourceLine";
1105 return -1;
1106 }
1107 }
1108
1116 public function setDeliveryDate($user, $delivery_date)
1117 {
1118 if ($user->hasRight('expedition', 'creer')) {
1119 $sql = "UPDATE ".MAIN_DB_PREFIX."delivery";
1120 $sql .= " SET date_delivery = ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : 'null');
1121 $sql .= " WHERE rowid = ".((int) $this->id);
1122
1123 dol_syslog(get_class($this)."::setDeliveryDate", LOG_DEBUG);
1124 $resql = $this->db->query($sql);
1125 if ($resql) {
1126 $this->date_delivery = $delivery_date;
1127 return 1;
1128 } else {
1129 $this->error = $this->db->error();
1130 return -1;
1131 }
1132 } else {
1133 return -2;
1134 }
1135 }
1136
1147 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1148 {
1149 global $langs;
1150
1151 $langs->load("sendings");
1152 $outputlangs->load("products");
1153
1154 if (!dol_strlen($modele)) {
1155 $modele = 'typhon';
1156
1157 if ($this->model_pdf) {
1158 $modele = $this->model_pdf;
1159 } elseif (getDolGlobalString('DELIVERY_ADDON_PDF')) {
1160 $modele = getDolGlobalString('DELIVERY_ADDON_PDF');
1161 }
1162 }
1163
1164 $modelpath = "core/modules/delivery/doc/";
1165
1166 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1167 }
1168
1177 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
1178 {
1179 $tables = array(
1180 'delivery'
1181 );
1182
1183 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
1184 }
1185
1194 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
1195 {
1196 $tables = array(
1197 'deliverydet'
1198 );
1199
1200 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
1201 }
1202}
1203
1204
1209{
1213 public $db;
1214
1218 public $element = 'deliverydet';
1219
1223 public $table_element = 'deliverydet';
1224
1228 public $label;
1229
1233 public $description;
1234
1240 public $ref;
1246 public $libelle;
1247
1248 // From llx_expeditiondet
1252 public $qty;
1253
1257 public $qty_asked;
1258
1262 public $qty_shipped;
1263
1267 public $fk_product;
1271 public $product_desc;
1275 public $product_type;
1279 public $product_ref;
1283 public $product_label;
1284
1288 public $price;
1289
1293 public $fk_origin_line;
1297 public $origin_id;
1298
1302 public $origin_line_id;
1303
1309 public $commande_ligne_id;
1310
1311
1317 public function __construct($db)
1318 {
1319 $this->db = $db;
1320 }
1321}
$object ref
Definition info.php:90
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...
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='')
Set status of an object.
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.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
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).
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage receptions.
create_line($origin_id, $qty, $fk_product, $description, $array_options=[])
Create a line.
__construct($db)
Constructor.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
deleteLine($lineid)
Delete line.
setDeliveryDate($user, $delivery_date)
Set the planned delivery date.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create object on disk.
update_line($id, $array_options=[])
Update a livraison line (only extrafields)
addline($origin_id, $qty, $array_options=[])
Add line.
fetch($id)
Load a delivery receipt.
create($user)
Create delivery receipt in database.
initAsSpecimen()
Initialise an instance with random values.
create_from_sending($user, $sending_id)
Creating the delivery slip from an existing shipment.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getTooltipContentArray($params)
getTooltipContentArray
getLibStatut($mode=0)
Return the label of the status.
LibStatut($status, $mode)
Return the label of a given status.
fetch_lines()
Load lines insto $this->lines.
getRemainingDelivered()
Get data list of Products remaining to be delivered for an order (with qty)
getNomUrl($withpicto=0, $save_lastsearch_value=-1)
Return clickable name (with picto eventually)
Management class of delivery note lines.
__construct($db)
Constructor.
Class to manage Dolibarr database access.
Class to manage third parties objects (customers, suppliers, prospects...)
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
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.
dol_delete_dir($dir, $nophperrors=0)
Remove a directory (not recursive, so content must be empty).
dol_dir_list($utf8_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:65
dol_now($mode='gmt')
Return date for now.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return a 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.
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:141