dolibarr 25.0.0-alpha
expeditionligne.class.php
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-2020 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-2024 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
14 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2020 Lenin Rivas <lenin@leninrivas.com>
16 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
17 * Copyright (C) 2025 Nick Fragoulis
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
40require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionlinebatch.class.php';
41
46{
50 public $element = 'expeditiondet';
51
55 public $table_element = 'expeditiondet';
56
60 public $parent_element = 'expedition';
61
65 public $fk_parent_attribute = 'fk_expedition';
66
73 public $line_id; // deprecated
74
78 public $fk_element;
79
83 public $origin_id;
84
88 public $fk_elementdet;
89
93 public $origin_line_id;
94
98 public $fk_parent;
99
103 public $element_type;
104
105
112 public $fk_origin; // Example: 'orderline'
113
117 public $fk_expedition;
118
122 public $db;
123
127 public $qty;
128
132 public $qty_shipped;
133
137 public $fk_product;
138
144 public $detail_batch;
145
150 public $detail_children;
151
156 public $details_entrepot;
157
158
162 public $entrepot_id;
163
168 public $fk_entrepot;
169
170
174 public $qty_asked;
175
181 public $ref;
182
186 public $product_ref;
187
193 public $libelle;
194
198 public $product_label;
199
205 public $desc;
206
210 public $product_desc;
211
216 public $product_type = 0;
217
221 public $rang;
222
226 public $weight;
227
231 public $weight_units;
232
236 public $length;
237
241 public $length_units;
242
246 public $width;
247
251 public $width_units;
252
256 public $height;
257
261 public $height_units;
262
266 public $surface;
267
271 public $surface_units;
272
276 public $volume;
277
281 public $volume_units;
282
288 public $stockable_product = 1;
289
293 public $remise_percent;
294
298 public $tva_tx;
299
303 public $total_ht;
304
308 public $total_ttc;
309
313 public $total_tva;
314
318 public $total_localtax1;
319
323 public $total_localtax2;
324
325
331 public function __construct($db)
332 {
333 $this->db = $db;
334 }
335
342 public function fetch($rowid)
343 {
344 $sql = 'SELECT ed.rowid, ed.fk_expedition, ed.fk_entrepot, ed.fk_product, ed.fk_parent, ed.description, ed.fk_unit, ed.fk_elementdet, ed.element_type, ed.qty, ed.rang, ed.extraparams';
345 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as ed';
346 $sql .= ' WHERE ed.rowid = '.((int) $rowid);
347 $result = $this->db->query($sql);
348 if ($result) {
349 $objp = $this->db->fetch_object($result);
350 $this->id = $objp->rowid;
351 $this->fk_expedition = $objp->fk_expedition;
352 $this->entrepot_id = $objp->fk_entrepot;
353 $this->fk_entrepot = $objp->fk_entrepot;
354 $this->fk_product = $objp->fk_product;
355 $this->fk_parent = $objp->fk_parent;
356 $this->description = $objp->description;
357 $this->fk_unit = $objp->fk_unit;
358 $this->fk_elementdet = $objp->fk_elementdet;
359 $this->element_type = $objp->element_type;
360 $this->qty = $objp->qty;
361 $this->rang = $objp->rang;
362
363 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
364
365 $this->db->free($result);
366
367 return 1;
368 } else {
369 $this->errors[] = $this->db->lasterror();
370 $this->error = $this->db->lasterror();
371 return -1;
372 }
373 }
374
382 public function insert($user = null, $notrigger = 0)
383 {
384 global $langs;
385 $error = 0;
386
387 $skip_check_parameters = false;
388
389 // Handling parent line subtotal line
390 if (!empty($this->element_type)
391 && !empty($this->fk_elementdet)
392 && $this->element_type == 'commande') {
393 $objectsrc_line = new OrderLine($this->db);
394 $objectsrc_line->fetch($this->fk_elementdet);
395 $skip_check_parameters = $objectsrc_line->special_code == SUBTOTALS_SPECIAL_CODE;
396 }
397
398 // Check parameters
399 $origin_id = $this->origin_id;
400 if ($origin_id > 0) {
401 if ((empty($this->fk_expedition)
402 || (empty($this->fk_elementdet) && empty($this->fk_parent)) // at least origin line id of parent line id is set
403 || !is_numeric($this->qty))
404 && !$skip_check_parameters) {
405 $langs->load('errors');
406 $this->errors[] = $langs->trans('ErrorMandatoryParametersNotProvided');
407 return -1;
408 }
409 } else {
410 if (empty($this->fk_expedition) || !is_numeric($this->qty)) {
411 $langs->load('errors');
412 $this->errors[] = $langs->trans('ErrorMandatoryParametersNotProvided');
413 return -1;
414 }
415 }
416 $this->db->begin();
417
418 if (getDolGlobalString('STOCK_EXPEDITION_NO_MORE_THAN_ORDER') && !empty($this->fk_elementdet) && !empty($this->fk_expedition)) {
419 $this->checkQtyVsOrderLine($this->fk_elementdet, $this->qty, 0);
420 if (!empty($this->error)) {
421 $this->db->rollback();
422 return -5;
423 }
424 }
425
426 if (empty($this->rang)) {
427 $this->rang = 0;
428 }
429
430 // Rank to use
431 $ranktouse = $this->rang;
432 if ($ranktouse == -1) {
433 $rangmax = $this->line_max($this->fk_expedition);
434 $ranktouse = $rangmax + 1;
435 }
436
437 $sql = "INSERT INTO ".MAIN_DB_PREFIX."expeditiondet (";
438 $sql .= "fk_expedition";
439 $sql .= ", fk_entrepot";
440 $sql .= ", fk_elementdet";
441 $sql .= ", fk_parent";
442 $sql .= ", fk_product";
443 $sql .= ", element_type";
444 $sql .= ", qty";
445 $sql .= ", fk_unit";
446 $sql .= ", description";
447 $sql .= ", rang";
448 $sql .= ") VALUES (";
449 $sql .= ((int) $this->fk_expedition);
450 $sql .= ", ".(empty($this->entrepot_id) ? 'NULL' : ((int) $this->entrepot_id));
451 $sql .= ", ".(empty($this->fk_elementdet) ? 'NULL' : ((int) $this->fk_elementdet));
452 $sql .= ", ".(empty($this->fk_parent) ? 'NULL' : ((int) $this->fk_parent));
453 $sql .= ", ".(empty($this->fk_product) ? 'NULL' : ((int) $this->fk_product));
454 $sql .= ", '".(empty($this->element_type) ? 'order' : $this->db->escape($this->element_type))."'";
455 $sql .= ", ".price2num($this->qty, 'MS');
456 $sql .= ", ".((int) $this->fk_unit);
457 $sql .= ", '".(empty($this->description) ? '' : $this->db->escape($this->description))."'";
458 $sql .= ", ".((int) $ranktouse);
459 $sql .= ")";
460
461 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
462 $resql = $this->db->query($sql);
463 if ($resql) {
464 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."expeditiondet");
465
466 if (!$error) {
467 $result = $this->insertExtraFields();
468 if ($result < 0) {
469 $error++;
470 }
471 }
472
473 if (!$error && !$notrigger) {
474 // Call trigger
475 $result = $this->call_trigger('LINESHIPPING_INSERT', $user);
476 if ($result < 0) {
477 $error++;
478 }
479 // End call triggers
480 }
481
482 if ($error) {
483 foreach ($this->errors as $errmsg) {
484 dol_syslog(__METHOD__.' '.$errmsg, LOG_ERR);
485 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
486 }
487 }
488 } else {
489 $error++;
490 }
491
492 if ($error) {
493 $this->db->rollback();
494 return -1;
495 } else {
496 $this->db->commit();
497 return $this->id;
498 }
499 }
500
509 public function findAllChild($line_id, &$list = array(), $mode = 0)
510 {
511 if ($line_id > 0) {
512 // find all child
513 $sql = "SELECT ed.rowid as child_line_id";
514 if ($mode == 1) {
515 $sql .= ", ed.fk_product";
516 $sql .= ", ed.fk_parent";
517 $sql .= ", " . $this->db->ifsql('eb.rowid IS NULL', 'ed.qty', 'eb.qty') . " as qty";
518 $sql .= ", " . $this->db->ifsql('eb.rowid IS NULL', 'ed.fk_entrepot', 'eb.fk_warehouse') . " as fk_warehouse";
519 $sql .= ", eb.batch, eb.eatby, eb.sellby";
520 }
521 $sql .= " FROM " . $this->db->prefix() . $this->table_element . " as ed";
522 $sql .= " LEFT JOIN " . $this->db->prefix() . "expeditiondet_batch as eb ON eb.fk_expeditiondet = " . ((int) $line_id);
523 $sql .= " WHERE ed.fk_parent = " . ((int) $line_id);
524 $sql .= $this->db->order('ed.fk_product,ed.rowid', 'ASC,ASC');
525
526 $resql = $this->db->query($sql);
527 if ($resql) {
528 while ($obj = $this->db->fetch_object($resql)) {
529 $child_line_id = (int) $obj->child_line_id;
530 if (!isset($list[$line_id])) {
531 $list[$line_id] = array();
532 }
533
534 if ($mode == 0) {
535 $list[$line_id][] = $child_line_id;
536 } elseif ($mode == 1) {
537 $line_obj = new stdClass();
538 $line_obj->rowid = $child_line_id;
539 $line_obj->fk_product = $obj->fk_product;
540 $line_obj->fk_parent = $obj->fk_parent;
541 $line_obj->qty = $obj->qty;
542 $line_obj->fk_warehouse = $obj->fk_warehouse;
543 $line_obj->batch = $obj->batch;
544 $line_obj->eatby = $obj->eatby;
545 $line_obj->sellby = $obj->sellby;
546 $line_obj->iskit = 0;
547 $line_obj->incdec = 0;
548 $list[$line_id][] = $line_obj;
549 }
550
551 $this->findAllChild($child_line_id, $list, $mode);
552 }
553 $this->db->free($resql);
554 } else {
555 $this->error = $this->db->lasterror();
556 $this->errors[] = $this->error;
557 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
558 }
559 }
560
561 return 1;
562 }
563
571 public function delete($user = null, $notrigger = 0)
572 {
573 $error = 0;
574
575 $this->db->begin();
576
577 // virtual products : delete all children and batch
578 // Standalone dispatch allocations also belong to their source line.
579 if ((getDolGlobalInt('PRODUIT_SOUSPRODUITS') || $this->element_type == 'shipping') && !($this->fk_parent > 0)) {
580 // find all children
581 $line_id_list = array();
582 $result = $this->findAllChild($this->id, $line_id_list);
583 if ($result) {
584 $child_line_id_list = array_reverse($line_id_list, true);
585 foreach ($child_line_id_list as $child_line_id_arr) {
586 foreach ($child_line_id_arr as $child_line_id) {
587 // delete batch expedition line
588 if (isModEnabled('productbatch')) {
589 $sql = "DELETE FROM " . $this->db->prefix() . "expeditiondet_batch";
590 $sql .= " WHERE fk_expeditiondet = " . ((int) $child_line_id);
591 if (!$this->db->query($sql)) {
592 $error++;
593 $this->errors[] = $this->db->lasterror() . " - sql=$sql";
594 }
595 }
596
597 $sql = "DELETE FROM " . $this->db->prefix() . "expeditiondet";
598 $sql .= " WHERE rowid = " . ((int) $child_line_id);
599 if (!$this->db->query($sql)) {
600 $error++;
601 $this->errors[] = $this->db->lasterror() . " - sql=$sql";
602 }
603
604 if ($error) {
605 break;
606 }
607 }
608 if ($error) {
609 break;
610 }
611 }
612 } else {
613 $error++;
614 }
615 }
616
617 if (!$error) {
618 // delete batch expedition line
619 if (isModEnabled('productbatch')) {
620 $sql = "DELETE FROM ".$this->db->prefix()."expeditiondet_batch";
621 $sql .= " WHERE fk_expeditiondet = ".((int) $this->id);
622
623 if (!$this->db->query($sql)) {
624 $this->errors[] = $this->db->lasterror()." - sql=$sql";
625 $error++;
626 }
627 }
628
629 $sql = "DELETE FROM ".$this->db->prefix()."expeditiondet";
630 $sql .= " WHERE rowid = ".((int) $this->id);
631
632 if (!$error && $this->db->query($sql)) {
633 // Remove extrafields
634 if (!$error) {
635 $result = $this->deleteExtraFields();
636 if ($result < 0) {
637 $this->errors[] = $this->error;
638 $error++;
639 }
640 }
641 if (!$error && !$notrigger) {
642 // Call trigger
643 $result = $this->call_trigger('LINESHIPPING_DELETE', $user);
644 if ($result < 0) {
645 $this->errors[] = $this->error;
646 $error++;
647 }
648 // End call triggers
649 }
650 } else {
651 $this->errors[] = $this->db->lasterror()." - sql=$sql";
652 $error++;
653 }
654 }
655
656 if (!$error) {
657 $this->db->commit();
658 return 1;
659 } else {
660 foreach ($this->errors as $errmsg) {
661 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
662 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
663 }
664 $this->db->rollback();
665 return -1 * $error;
666 }
667 }
668
676 public function update($user = null, $notrigger = 0)
677 {
678 global $langs;
679 $error = 0;
680
681 dol_syslog(get_class($this)."::update id=$this->id, entrepot_id=$this->entrepot_id, product_id=$this->fk_product, qty=$this->qty");
682
683 $this->db->begin();
684
685 if (!empty($this->id) && empty($this->fk_elementdet)) {
686 $sql = "SELECT fk_elementdet FROM ".MAIN_DB_PREFIX."expeditiondet";
687 $sql .= " WHERE rowid = ".((int) $this->id);
688 $resql = $this->db->query($sql);
689 if ($resql) {
690 $obj = $this->db->fetch_object($resql);
691 if ($obj) {
692 $this->fk_elementdet = $obj->fk_elementdet;
693 }
694 }
695 }
696
697 if (getDolGlobalString('STOCK_EXPEDITION_NO_MORE_THAN_ORDER') && !empty($this->fk_elementdet)) {
698 $qty_to_check = $this->qty;
699 if (!empty($this->detail_batch)) {
700 if (is_array($this->detail_batch)) {
701 $qty_to_check = array_sum(array_column($this->detail_batch, 'qty'));
702 } else {
703 $qty_to_check = $this->detail_batch->qty;
704 }
705 }
706 $this->checkQtyVsOrderLine($this->fk_elementdet, $qty_to_check, $this->id);
707 if (!empty($this->error)) {
708 $this->db->rollback();
709 return -5;
710 }
711 }
712
713 // Clean parameters
714 if (empty($this->qty)) {
715 $this->qty = 0;
716 }
717 $qty = price2num($this->qty);
718 $fk_unit = $this->fk_unit;
719 $remainingQty = 0;
720 $batch = null;
721 $batch_id = 0;
722 $expedition_batch_id = 0;
723 $origin_id = $this->origin_id;
724 if ($origin_id > 0) {
725 if (is_array($this->detail_batch)) { // array of ExpeditionLineBatch
726 if (count($this->detail_batch) > 1) {
727 dol_syslog(get_class($this).'::update only possible for one batch', LOG_ERR);
728 $this->errors[] = 'ErrorBadParameters';
729 $error++;
730 } else {
731 $batch = $this->detail_batch[0]->batch;
732 $batch_id = $this->detail_batch[0]->fk_origin_stock;
733 $expedition_batch_id = $this->detail_batch[0]->id;
734 if ($this->entrepot_id != $this->detail_batch[0]->entrepot_id) {
735 dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
736 $this->errors[] = 'ErrorBadParameters';
737 $error++;
738 }
739 $qty = price2num($this->detail_batch[0]->qty);
740 }
741 } elseif (!empty($this->detail_batch)) {
742 $batch = $this->detail_batch->batch;
743 $batch_id = $this->detail_batch->fk_origin_stock;
744 $expedition_batch_id = $this->detail_batch->id;
745 if ($this->entrepot_id != $this->detail_batch->entrepot_id) {
746 dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
747 $this->errors[] = 'ErrorBadParameters';
748 $error++;
749 }
750 $qty = price2num($this->detail_batch->qty);
751 }
752
753 // check parameters
754 if (!isset($this->id) || !isset($this->entrepot_id)) {
755 dol_syslog(get_class($this).'::update missing line id and/or warehouse id', LOG_ERR);
756 $langs->load('errors');
757 $this->errors[] = $langs->trans('ErrorMandatoryParametersNotProvided');
758 $error++;
759 return -1;
760 }
761
762 // update lot
763 if (!empty($batch) && isModEnabled('productbatch')) {
764 $batch_id_str = $batch_id ?? 'null';
765 dol_syslog(get_class($this)."::update expedition batch id=$expedition_batch_id, batch_id=$batch_id_str, batch=$batch");
766
767 if (empty($batch_id) || empty($this->fk_product)) {
768 dol_syslog(get_class($this).'::update missing fk_origin_stock (batch_id) and/or fk_product', LOG_ERR);
769 $langs->load('errors');
770 $this->errors[] = $langs->trans('ErrorMandatoryParametersNotProvided');
771 $error++;
772 }
773
774 // fetch remaining lot qty
775 $shipmentlinebatch = new ExpeditionLineBatch($this->db);
776 $lotArray = $shipmentlinebatch->fetchAll($this->id);
777 if (!$error && $lotArray < 0) {
778 $this->errors[] = $this->db->lasterror()." - ExpeditionLineBatch::fetchAll";
779 $error++;
780 } else {
781 // calculate new total line qty
782 foreach ($lotArray as $lot) {
783 if ($expedition_batch_id != $lot->id) {
784 $remainingQty += $lot->qty;
785 }
786 }
787 $qty += $remainingQty;
788
789 //fetch lot details
790 // fetch from product_lot
791 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
792 $lot = new Productlot($this->db);
793 if ($lot->fetch(0, $this->fk_product, $batch) < 0) {
794 $this->errors = array_merge($this->errors, $lot->errors);
795 $error++;
796 }
797 if (!$error && !empty($expedition_batch_id)) {
798 // delete lot expedition line
799 $sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
800 $sql .= " WHERE fk_expeditiondet = ".((int) $this->id);
801 $sql .= " AND rowid = ".((int) $expedition_batch_id);
802
803 if (!$this->db->query($sql)) {
804 $this->errors[] = $this->db->lasterror()." - sql=$sql";
805 $error++;
806 }
807 }
808 if (!$error && $this->detail_batch->qty > 0) {
809 // create lot expedition line
810 if (isset($lot->id)) {
811 $shipmentLot = new ExpeditionLineBatch($this->db);
812 $shipmentLot->batch = $lot->batch;
813 $shipmentLot->eatby = $lot->eatby;
814 $shipmentLot->sellby = $lot->sellby;
815 $shipmentLot->fk_warehouse = $this->detail_batch->entrepot_id;
816 $shipmentLot->qty = $this->detail_batch->qty;
817 $shipmentLot->fk_origin_stock = (int) $batch_id;
818 if ($shipmentLot->create($this->id) < 0) {
819 $this->errors = $shipmentLot->errors;
820 $error++;
821 }
822 }
823 }
824 }
825 }
826 }
827 if (!$error) {
828 // update line
829 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
830 $sql .= " fk_entrepot = ".($this->entrepot_id > 0 ? ((int) $this->entrepot_id) : 'null');
831 $sql .= " , qty = ".((float) price2num($qty, 'MS'));
832 $sql .= " , fk_unit = ".((int) $this->fk_unit);
833 $sql .= " WHERE rowid = ".((int) $this->id);
834
835 if (!$this->db->query($sql)) {
836 $this->errors[] = $this->db->lasterror()." - sql=$sql";
837 $error++;
838 }
839 }
840
841 if (!$error) {
842 $result = $this->insertExtraFields();
843 if ($result < 0) {
844 $this->errors[] = $this->error;
845 $error++;
846 }
847 }
848
849 if (!$error && !$notrigger) {
850 // Call trigger
851 $result = $this->call_trigger('LINESHIPPING_MODIFY', $user);
852 if ($result < 0) {
853 $this->errors[] = $this->error;
854 $error++;
855 }
856 // End call triggers
857 }
858 if (!$error) {
859 $this->db->commit();
860 return 1;
861 } else {
862 foreach ($this->errors as $errmsg) {
863 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
864 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
865 }
866 $this->db->rollback();
867 return -1 * $error;
868 }
869 }
870
880 private function checkQtyVsOrderLine(int $fk_elementdet, float $qty, int $exclude_line_id): bool
881 {
882 global $langs;
883
884 // Ordered quantity on the source order line
885 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
886 $orderline = new OrderLine($this->db);
887 if ($orderline->fetch($fk_elementdet) <= 0) {
888 return true; // Order line not found, skip check
889 }
890 if ($orderline->product_type == 9) {
891 return true; // Skip check for title/separator lines, they have no deliverable quantity
892 }
893 $qty_ordered = (float) $orderline->qty;
894
895 // Quantity already shipped on other shipment lines
896 $sql = "SELECT COALESCE(SUM(ed.qty), 0) as qty_shipped";
897 $sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
898 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expedition as e ON e.rowid = ed.fk_expedition";
899 $sql .= " WHERE ed.fk_elementdet = ".((int) $fk_elementdet);
900 $sql .= " AND e.fk_statut >= 0"; // exclude only deleted ones
901 if ($exclude_line_id > 0) {
902 $sql .= " AND ed.rowid != ".((int) $exclude_line_id); // exclude current line being edited
903 }
904
905 $resql = $this->db->query($sql);
906 if (!$resql) {
907 return true;
908 }
909 $obj = $this->db->fetch_object($resql);
910 $qty_already_shipped = $obj ? (float) $obj->qty_shipped : 0;
911
912 $qty_remaining = $qty_ordered - $qty_already_shipped;
913
914 if ($qty > $qty_remaining) {
915 $langs->load('errors');
916 $product_ref = !empty($orderline->product_ref) ? $orderline->product_ref : $orderline->desc;
917 $this->error = $langs->trans('ErrorExpeditionQtyTooHigh', $qty, max(0, $qty_remaining), $product_ref);
918 return false;
919 }
920
921 return true;
922 }
923}
deleteExtraFields()
Delete all extra fields values for the current object.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage lines of shipment.
insert($user=null, $notrigger=0)
Insert line into database.
fetch($rowid)
Load line expedition.
findAllChild($line_id, &$list=array(), $mode=0)
Find all children.
__construct($db)
Constructor.
checkQtyVsOrderLine(int $fk_elementdet, float $qty, int $exclude_line_id)
Check that qty to ship does not exceed remaining qty on order line.
update($user=null, $notrigger=0)
Update a line in database.
CRUD class for batch number management within shipment.
Class to manage order lines.
Class with list of lots and properties.
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.