dolibarr 25.0.0-alpha
api_mos.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3 * Copyright (C) 2019 Maxime Kohlhaas <maxime@atm-consulting.fr>
4 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21use Luracast\Restler\RestException;
22
23require_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
24require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
25
26
39class Mos extends DolibarrApi
40{
44 public $mo;
45
49 public function __construct()
50 {
51 global $db, $conf;
52 $this->db = $db;
53 $this->mo = new Mo($this->db);
54 }
55
67 public function get($id)
68 {
69 if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
70 throw new RestException(403);
71 }
72
73 $result = $this->mo->fetch($id);
74 if (!$result) {
75 throw new RestException(404, 'MO not found');
76 }
77
78 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
79 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
80 }
81
82 return $this->_cleanObjectDatas($this->mo);
83 }
84
102 public function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
103 {
104 if (!DolibarrApiAccess::$user->hasRight('categorie', 'lire')) {
105 throw new RestException(403);
106 }
107
108 $categories = new Categorie($this->db);
109
110 $result = $categories->getListForItem($id, Categorie::TYPE_MO, $sortfield, $sortorder, $limit, $page);
111
112 if ($result < 0) {
113 throw new RestException(503, 'Error when retrieve category list : ' . implode(',', array_merge(array($categories->error), $categories->errors)));
114 }
115
116 return $result;
117 }
118
119
137 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
138 {
139 if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
140 throw new RestException(403);
141 }
142
143 $obj_ret = array();
144 $tmpobject = new Mo($this->db);
145
146 $socid = DolibarrApiAccess::$user->socid ?: 0;
147
148 $restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
149
150 // If the internal user must only see his customers, force searching by him
151 $search_sale = 0;
152 if ($restrictonsocid && !DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
153 $search_sale = DolibarrApiAccess::$user->id;
154 }
155
156 $sql = "SELECT t.rowid";
157 $sql .= " FROM ".MAIN_DB_PREFIX.$tmpobject->table_element." AS t";
158 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmpobject->table_element."_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields
159 $sql .= " WHERE 1 = 1";
160 if ($tmpobject->ismultientitymanaged) {
161 $sql .= ' AND t.entity IN ('.getEntity($tmpobject->element).')';
162 }
163 if ($restrictonsocid && $socid) {
164 $sql .= " AND t.fk_soc = ".((int) $socid);
165 }
166 // Search on sale representative
167 if ($search_sale && $search_sale != '-1') {
168 if ($search_sale == -2) {
169 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
170 } elseif ($search_sale > 0) {
171 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
172 }
173 }
174 if ($sqlfilters) {
175 $errormessage = '';
176 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
177 if ($errormessage) {
178 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
179 }
180 }
181
182 $sql .= $this->db->order($sortfield, $sortorder);
183 if ($limit) {
184 if ($page < 0) {
185 $page = 0;
186 }
187 $offset = $limit * $page;
188
189 $sql .= $this->db->plimit($limit + 1, $offset);
190 }
191
192 $result = $this->db->query($sql);
193 if ($result) {
194 $i = 0;
195 $num = $this->db->num_rows($result);
196 $min = min($num, ($limit <= 0 ? $num : $limit));
197 while ($i < $min) {
198 $obj = $this->db->fetch_object($result);
199 $tmp_object = new Mo($this->db);
200 if ($tmp_object->fetch($obj->rowid)) {
201 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($tmp_object), $properties);
202 }
203 $i++;
204 }
205 } else {
206 throw new RestException(503, 'Error when retrieve MO list');
207 }
208
209 return $obj_ret;
210 }
211
222 public function post($request_data = null)
223 {
224 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
225 throw new RestException(403);
226 }
227 // Check mandatory fields
228 $result = $this->_validate($request_data);
229
230 foreach ($request_data as $field => $value) {
231 if ($field === 'caller') {
232 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
233 $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
234 continue;
235 }
236
237 $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
238 }
239
240 $this->checkRefNumbering();
241
242 $result = $this->mo->create(DolibarrApiAccess::$user);
243 //var_dump($result);exit;
244 if ($result < 0) {
245 throw new RestException(500, "Error creating MO", array_merge(array($this->mo->error), $this->mo->errors));
246 }
247
248 return $this->mo->id;
249 }
250
260 public function put($id, $request_data = null)
261 {
262 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
263 throw new RestException(403);
264 }
265
266 $result = $this->mo->fetch($id);
267 if (!$result) {
268 throw new RestException(404, 'MO not found');
269 }
270
271 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
272 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
273 }
274
275 foreach ($request_data as $field => $value) {
276 if ($field == 'id') {
277 continue;
278 }
279 if ($field === 'caller') {
280 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
281 $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
282 continue;
283 }
284
285 if ($field == 'array_options' && is_array($value)) {
286 foreach ($value as $index => $val) {
287 $this->mo->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->mo);
288 }
289 continue;
290 }
291
292 $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
293 }
294
295 $this->checkRefNumbering();
296
297 if ($this->mo->update(DolibarrApiAccess::$user) > 0) {
298 return $this->get($id);
299 } else {
300 throw new RestException(500, $this->mo->error);
301 }
302 }
303
318 public function validate($id, $notrigger = 0)
319 {
320 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
321 throw new RestException(403);
322 }
323
324 $result = $this->mo->fetch($id);
325 if (!$result) {
326 throw new RestException(404, 'MO not found');
327 }
328
329 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
330 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
331 }
332
333 $result = $this->mo->validate(DolibarrApiAccess::$user, $notrigger);
334 if ($result == 0) {
335 throw new RestException(304, 'Error nothing done. May be object is already validated');
336 }
337 if ($result < 0) {
338 throw new RestException(500, 'Error when validating MO: '.$this->mo->error);
339 }
340 $result = $this->mo->fetch($id);
341
342 return $this->_cleanObjectDatas($this->mo);
343 }
344
359 public function confirmProduced($id, $notrigger = 0)
360 {
361 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
362 throw new RestException(403);
363 }
364
365 $result = $this->mo->fetch($id);
366 if (!$result) {
367 throw new RestException(404, 'MO not found');
368 }
369
370 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
371 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
372 }
373
374 $result = $this->mo->setStatut($this->mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
375 if ($result < 0) {
376 throw new RestException(500, 'Error when setting MO Produced: '.$this->mo->error);
377 }
378 $result = $this->mo->fetch($id);
379
380 return $this->_cleanObjectDatas($this->mo);
381 }
382
391 public function delete($id)
392 {
393 if (!DolibarrApiAccess::$user->hasRight('mrp', 'delete')) {
394 throw new RestException(403);
395 }
396 $result = $this->mo->fetch($id);
397 if (!$result) {
398 throw new RestException(404, 'MO not found');
399 }
400
401 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
402 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
403 }
404
405 if (!$this->mo->delete(DolibarrApiAccess::$user)) {
406 throw new RestException(500, 'Error when deleting MO : '.$this->mo->error);
407 }
408
409 return array(
410 'success' => array(
411 'code' => 200,
412 'message' => 'MO deleted'
413 )
414 );
415 }
416
417
450 public function produceAndConsumeAll($id, $request_data = null)
451 {
452 global $langs;
453
454 $error = 0;
455
456 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
457 throw new RestException(403, 'Not enough permission');
458 }
459 $result = $this->mo->fetch($id);
460 if (!$result) {
461 throw new RestException(404, 'MO not found');
462 }
463
464 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
465 throw new RestException(405, 'Error bad status of MO');
466 }
467
468 // Code for consume and produce...
469 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
470 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
471 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
472
473 $stockmove = new MouvementStock($this->db);
474
475 $labelmovement = '';
476 $codemovement = '';
477 $autoclose = 1;
478 $arraytoconsume = array();
479 $arraytoproduce = array();
480
481 foreach ($request_data as $field => $value) {
482 if ($field == 'inventorylabel') {
483 $labelmovement = $value;
484 }
485 if ($field == 'inventorycode') {
486 $codemovement = $value;
487 }
488 if ($field == 'autoclose') {
489 $autoclose = $value;
490 }
491 if ($field == 'arraytoconsume') {
492 $arraytoconsume = $value;
493 }
494 if ($field == 'arraytoproduce') {
495 $arraytoproduce = $value;
496 }
497 if ($field === 'caller') {
498 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
499 $stockmove->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
500 continue;
501 }
502 }
503
504 if (empty($labelmovement)) {
505 throw new RestException(500, "Field inventorylabel not provided");
506 }
507 if (empty($codemovement)) {
508 throw new RestException(500, "Field inventorycode not provided");
509 }
510
511 $consumptioncomplete = true;
512 $productioncomplete = true;
513
514 if (!empty($arraytoconsume) && !empty($arraytoproduce)) {
515 $pos = 0;
516 $arrayofarrayname = array("arraytoconsume", "arraytoproduce");
517 foreach ($arrayofarrayname as $arrayname) {
518 foreach (${$arrayname} as $value) {
519 $tmpproduct = new Product($this->db);
520 if (empty($value["objectid"])) {
521 throw new RestException(500, "Field objectid required in ".$arrayname);
522 }
523 $tmpproduct->fetch($value["qty"]);
524 if (empty($value["qty"])) {
525 throw new RestException(500, "Field qty required in ".$arrayname);
526 }
527 if ($value["qty"] != 0) {
528 $qtytoprocess = $value["qty"];
529 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
530 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
531 $error++;
532 throw new RestException(500, "Field fk_warehouse must be > 0 in ".$arrayname);
533 }
534 if ($tmpproduct->status_batch) {
535 $error++;
536 throw new RestException(500, "Product ".$tmpproduct->ref."must be in batch");
537 }
538 }
539 $idstockmove = 0;
540 if (!$error && $value["fk_warehouse"] > 0) {
541 // Record consumption to do and stock movement
542 $id_product_batch = 0;
543
544 $stockmove->setOrigin($this->mo->element, $this->mo->id);
545
546 if ($arrayname == 'arraytoconsume') {
547 $moline = new MoLine($this->db);
548 $moline->fk_mo = $this->mo->id;
549 $moline->position = $pos;
550 $moline->fk_product = $value["objectid"];
551 $moline->fk_warehouse = (int) $value["fk_warehouse"];
552 $moline->qty = $qtytoprocess;
553 $moline->batch = (string) $tmpproduct->status_batch;
554 $moline->role = 'toproduce';
555 $moline->fk_mrp_production = 0;
556 $moline->fk_stock_movement = $idstockmove;
557 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
558
559 $resultmoline = $moline->create(DolibarrApiAccess::$user);
560 if ($resultmoline <= 0) {
561 $error++;
562 throw new RestException(500, $moline->error);
563 }
564 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
565 } else {
566 $moline = new MoLine($this->db);
567 $moline->fk_mo = $this->mo->id;
568 $moline->position = $pos;
569 $moline->fk_product = $value["objectid"];
570 $moline->fk_warehouse = $value["fk_warehouse"];
571 $moline->qty = $qtytoprocess;
572 $moline->batch = (string) $tmpproduct->status_batch;
573 $moline->role = 'toconsume';
574 $moline->fk_mrp_production = 0;
575 $moline->fk_stock_movement = $idstockmove;
576 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
577
578 $resultmoline = $moline->create(DolibarrApiAccess::$user);
579 if ($resultmoline <= 0) {
580 $error++;
581 throw new RestException(500, $moline->error);
582 }
583 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
584 }
585 if ($idstockmove < 0) {
586 $error++;
587 throw new RestException(500, $stockmove->error);
588 }
589 }
590 if (!$error) {
591 // Record consumption done
592 $moline = new MoLine($this->db);
593 $moline->fk_mo = $this->mo->id;
594 $moline->position = $pos;
595 $moline->fk_product = $value["objectid"];
596 $moline->fk_warehouse = $value["fk_warehouse"];
597 $moline->qty = $qtytoprocess;
598 $moline->batch = (string) $tmpproduct->status_batch;
599 if ($arrayname == "arraytoconsume") {
600 $moline->role = 'consumed';
601 } else {
602 $moline->role = 'produced';
603 }
604 $moline->fk_mrp_production = 0;
605 $moline->fk_stock_movement = $idstockmove;
606 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
607
608 $resultmoline = $moline->create(DolibarrApiAccess::$user);
609 if ($resultmoline <= 0) {
610 $error++;
611 throw new RestException(500, $moline->error);
612 }
613
614 $pos++;
615 }
616 }
617 }
618 }
619 if (!$error) {
620 if ($autoclose <= 0) {
621 $consumptioncomplete = false;
622 $productioncomplete = false;
623 }
624 }
625 } else {
626 $pos = 0;
627 foreach ($this->mo->lines as $line) {
628 if ($line->role == 'toconsume') {
629 $tmpproduct = new Product($this->db);
630 $tmpproduct->fetch($line->fk_product);
631 if ($line->qty != 0) {
632 $qtytoprocess = $line->qty;
633 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
634 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
635 $langs->load("errors");
636 $error++;
637 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), (string) $tmpproduct->ref));
638 }
639 if ($tmpproduct->status_batch) {
640 $langs->load("errors");
641 $error++;
642 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), (string) $tmpproduct->ref));
643 }
644 }
645 $idstockmove = 0;
646 if (!$error && $line->fk_warehouse > 0) {
647 // Record stock movement
648 $id_product_batch = 0;
649 $stockmove->origin_type = 'mo';
650 $stockmove->origin_id = $this->mo->id;
651 if ($qtytoprocess >= 0) {
652 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, (int) $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
653 } else {
654 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, (int) $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
655 }
656 if ($idstockmove < 0) {
657 $error++;
658 throw new RestException(500, $stockmove->error);
659 }
660 }
661 if (!$error) {
662 // Record consumption
663 $moline = new MoLine($this->db);
664 $moline->fk_mo = $this->mo->id;
665 $moline->position = $pos;
666 $moline->fk_product = $line->fk_product;
667 $moline->fk_warehouse = $line->fk_warehouse;
668 $moline->qty = $qtytoprocess;
669 $moline->batch = (string) $tmpproduct->status_batch;
670 $moline->role = 'consumed';
671 $moline->fk_mrp_production = $line->id;
672 $moline->fk_stock_movement = $idstockmove;
673 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
674
675 $resultmoline = $moline->create(DolibarrApiAccess::$user);
676 if ($resultmoline <= 0) {
677 $error++;
678 throw new RestException(500, $moline->error);
679 }
680
681 $pos++;
682 }
683 }
684 }
685 }
686 $pos = 0;
687 foreach ($this->mo->lines as $line) {
688 if ($line->role == 'toproduce') {
689 $tmpproduct = new Product($this->db);
690 $tmpproduct->fetch($line->fk_product);
691 if ($line->qty != 0) {
692 $qtytoprocess = $line->qty;
693 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
694 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
695 $langs->load("errors");
696 $error++;
697 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), (string) $tmpproduct->ref));
698 }
699 if ($tmpproduct->status_batch) {
700 $langs->load("errors");
701 $error++;
702 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), (string) $tmpproduct->ref));
703 }
704 }
705 $idstockmove = 0;
706 if (!$error && $line->fk_warehouse > 0) {
707 // Record stock movement
708 $id_product_batch = 0;
709 $stockmove->origin_type = 'mo';
710 $stockmove->origin_id = $this->mo->id;
711 if ($qtytoprocess >= 0) {
712 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, (int) $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
713 } else {
714 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, (int) $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
715 }
716 if ($idstockmove < 0) {
717 $error++;
718 throw new RestException(500, $stockmove->error);
719 }
720 }
721 if (!$error) {
722 // Record consumption
723 $moline = new MoLine($this->db);
724 $moline->fk_mo = $this->mo->id;
725 $moline->position = $pos;
726 $moline->fk_product = $line->fk_product;
727 $moline->fk_warehouse = $line->fk_warehouse;
728 $moline->qty = $qtytoprocess;
729 $moline->batch = (string) $tmpproduct->status_batch;
730 $moline->role = 'produced';
731 $moline->fk_mrp_production = $line->id;
732 $moline->fk_stock_movement = $idstockmove;
733 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
734
735 $resultmoline = $moline->create(DolibarrApiAccess::$user);
736 if ($resultmoline <= 0) {
737 $error++;
738 throw new RestException(500, $moline->error);
739 }
740
741 $pos++;
742 }
743 }
744 }
745 }
746
747 if (!$error) {
748 if ($autoclose > 0) {
749 foreach ($this->mo->lines as $line) {
750 if ($line->role == 'toconsume') {
751 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
752 $alreadyconsumed = 0;
753 foreach ($arrayoflines as $line2) {
754 $alreadyconsumed += $line2['qty'];
755 }
756
757 if ($alreadyconsumed < $line->qty) {
758 $consumptioncomplete = false;
759 }
760 }
761 if ($line->role == 'toproduce') {
762 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
763 $alreadyproduced = 0;
764 foreach ($arrayoflines as $line2) {
765 $alreadyproduced += $line2['qty'];
766 }
767
768 if ($alreadyproduced < $line->qty) {
769 $productioncomplete = false;
770 }
771 }
772 }
773 } else {
774 $consumptioncomplete = false;
775 $productioncomplete = false;
776 }
777 }
778 }
779
780 // Update status of MO
781 dol_syslog("consumptioncomplete = ".json_encode($consumptioncomplete)." productioncomplete = ".json_encode($productioncomplete));
782 if ($consumptioncomplete && $productioncomplete) {
783 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
784 } else {
785 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
786 }
787 if ($result <= 0) {
788 throw new RestException(500, $this->mo->error);
789 }
790
791 return $this->mo->id;
792 }
793
828 public function produceAndConsume($id, $request_data = null)
829 {
830 if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
831 throw new RestException(403, 'Not enough permission');
832 }
833 $result = $this->mo->fetch($id);
834 if (!$result) {
835 throw new RestException(404, 'MO not found');
836 }
837
838 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
839 throw new RestException(405, 'Error bad status of MO');
840 }
841
842 // Code for consume and produce...
843 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
844 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
845 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
846
847 $stockmove = new MouvementStock($this->db);
848
849 $labelmovement = '';
850 $codemovement = '';
851 $autoclose = 1;
852 $arraytoconsume = array();
853 $arraytoproduce = array();
854
855 foreach ($request_data as $field => $value) {
856 if ($field == 'inventorylabel') {
857 $labelmovement = $value;
858 }
859 if ($field == 'inventorycode') {
860 $codemovement = $value;
861 }
862 if ($field == 'autoclose') {
863 $autoclose = $value;
864 }
865 if ($field == 'arraytoconsume') {
866 $arraytoconsume = $value;
867 }
868 if ($field == 'arraytoproduce') {
869 $arraytoproduce = $value;
870 }
871 if ($field === 'caller') {
872 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
873 $stockmove->context['caller'] = $request_data['caller'];
874 continue;
875 }
876 }
877
878 if (empty($labelmovement)) {
879 throw new RestException(500, "Field inventorylabel not provided");
880 }
881 if (empty($codemovement)) {
882 throw new RestException(500, "Field inventorycode not provided");
883 }
884
885 $this->db->begin();
886
887 $pos = 0;
888 $arrayofarrayname = array("arraytoconsume", "arraytoproduce");
889 foreach ($arrayofarrayname as $arrayname) {
890 foreach (${$arrayname} as $value) {
891 if (empty($value["objectid"])) {
892 throw new RestException(500, "Field objectid required in " . $arrayname);
893 }
894
895 $molinetoprocess = new MoLine($this->db);
896 $tmpmolineid = $molinetoprocess->fetch($value["objectid"]);
897 if ($tmpmolineid <= 0) {
898 throw new RestException(500, "MoLine with rowid " . $value["objectid"] . " not exist.");
899 }
900
901 $tmpproduct = new Product($this->db);
902 $tmpproduct->fetch($molinetoprocess->fk_product);
903 if ($tmpproduct->status_batch) {
904 throw new RestException(500, "Product " . $tmpproduct->ref . " must be in batch, this API can't handle it currently.");
905 }
906
907 if (empty($value["qty"]) && $value["qty"] != 0) {
908 throw new RestException(500, "Field qty with lower or higher then 0 required in " . $arrayname);
909 }
910 $qtytoprocess = $value["qty"];
911
912 $fk_warehousetoprocess = 0;
913 if ($molinetoprocess->disable_stock_change == false) {
914 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
915 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
916 throw new RestException(500, "Field fk_warehouse required in " . $arrayname);
917 }
918 }
919 $fk_warehousetoprocess = (int) $value["fk_warehouse"];
920 }
921
922 $pricetoproduce = 0;
923 if (isset($value["pricetoproduce"])) { // If there is a price to produce set.
924 if ($value["pricetoproduce"] > 0) { // Only use prices grater then 0.
925 $pricetoproduce = $value["pricetoproduce"];
926 }
927 }
928
929 $idstockmove = 0;
930
931 if ($molinetoprocess->disable_stock_change == false) {
932 // Record stock movement
933 $id_product_batch = 0;
934 $stockmove->origin_type = 'mo';
935 $stockmove->origin_id = $this->mo->id;
936 if ($arrayname == "arraytoconsume") {
937 if ($qtytoprocess >= 0) {
938 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
939 } else {
940 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
941 }
942 } else {
943 if ($qtytoprocess >= 0) {
944 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, $pricetoproduce, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
945 } else {
946 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
947 }
948 }
949 if ($idstockmove <= 0) {
950 throw new RestException(500, $stockmove->error);
951 }
952 }
953
954 // Record consumption
955 $moline = new MoLine($this->db);
956 $moline->fk_mo = $this->mo->id;
957 $moline->position = $pos;
958 $moline->fk_product = $tmpproduct->id;
959 $moline->fk_warehouse = $idstockmove > 0 ? $fk_warehousetoprocess : null;
960 $moline->qty = $qtytoprocess;
961 $moline->batch = '';
962 $moline->fk_mrp_production = $molinetoprocess->id;
963 $moline->fk_stock_movement = $idstockmove > 0 ? $idstockmove : null;
964 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
965
966 if ($arrayname == "arraytoconsume") {
967 $moline->role = 'consumed';
968 } else {
969 $moline->role = 'produced';
970 }
971
972 $resultmoline = $moline->create(DolibarrApiAccess::$user);
973 if ($resultmoline <= 0) {
974 throw new RestException(500, $moline->error);
975 }
976
977 $pos++;
978 }
979 }
980
981 $consumptioncomplete = true;
982 $productioncomplete = true;
983
984 if ($autoclose > 0) {
985 // Refresh Lines after consumptions.
986 $this->mo->fetchLines();
987
988 foreach ($this->mo->lines as $line) {
989 if ($line->role == 'toconsume') {
990 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
991 $alreadyconsumed = 0;
992 foreach ($arrayoflines as $line2) {
993 $alreadyconsumed += $line2['qty'];
994 }
995
996 if ($alreadyconsumed < $line->qty) {
997 $consumptioncomplete = false;
998 }
999 }
1000 if ($line->role == 'toproduce') {
1001 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
1002 $alreadyproduced = 0;
1003 foreach ($arrayoflines as $line2) {
1004 $alreadyproduced += $line2['qty'];
1005 }
1006
1007 if ($alreadyproduced < $line->qty) {
1008 $productioncomplete = false;
1009 }
1010 }
1011 }
1012 } else {
1013 $consumptioncomplete = false;
1014 $productioncomplete = false;
1015 }
1016
1017 // Update status of MO
1018 dol_syslog("consumptioncomplete = " . (string) $consumptioncomplete . " productioncomplete = " . (string) $productioncomplete);
1019 //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
1020 if ($consumptioncomplete && $productioncomplete) {
1021 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
1022 } else {
1023 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
1024 }
1025 if ($result <= 0) {
1026 throw new RestException(500, $this->mo->error);
1027 }
1028
1029 $this->db->commit();
1030 return $this->mo->id;
1031 }
1032
1033
1034 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1044 protected function _cleanObjectDatas($object)
1045 {
1046 // phpcs:enable
1047 $object = parent::_cleanObjectDatas($object);
1048
1049 unset($object->rowid);
1050 unset($object->canvas);
1051
1052 unset($object->name);
1053 unset($object->lastname);
1054 unset($object->firstname);
1055 unset($object->civility_id);
1056 unset($object->statut);
1057 unset($object->state);
1058 unset($object->state_id);
1059 unset($object->state_code);
1060 unset($object->region);
1061 unset($object->region_code);
1062 unset($object->country);
1063 unset($object->country_id);
1064 unset($object->country_code);
1065 unset($object->barcode_type);
1066 unset($object->barcode_type_code);
1067 unset($object->barcode_type_label);
1068 unset($object->barcode_type_coder);
1069 unset($object->total_ht);
1070 unset($object->total_tva);
1071 unset($object->total_localtax1);
1072 unset($object->total_localtax2);
1073 unset($object->total_ttc);
1074 unset($object->fk_account);
1075 unset($object->comments);
1076 unset($object->note);
1077 unset($object->mode_reglement_id);
1078 unset($object->cond_reglement_id);
1079 unset($object->cond_reglement);
1080 unset($object->shipping_method_id);
1081 unset($object->fk_incoterms);
1082 unset($object->label_incoterms);
1083 unset($object->location_incoterms);
1084
1085 // If object has lines, remove $db property
1086 if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
1087 $nboflines = count($object->lines);
1088 for ($i = 0; $i < $nboflines; $i++) {
1089 $this->_cleanObjectDatas($object->lines[$i]);
1090
1091 unset($object->lines[$i]->lines);
1092 unset($object->lines[$i]->note);
1093 }
1094 }
1095
1096 return $object;
1097 }
1098
1107 private function _validate($data)
1108 {
1109 $myobject = array();
1110 foreach ($this->mo->fields as $field => $propfield) {
1111 if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || empty($propfield['notnull']) || $propfield['notnull'] != 1) {
1112 continue; // Not a mandatory field
1113 }
1114 if (!isset($data[$field])) {
1115 throw new RestException(400, "$field field missing");
1116 }
1117 $myobject[$field] = $data[$field];
1118 }
1119 return $myobject;
1120 }
1121
1127 private function checkRefNumbering()
1128 {
1129 $ref = substr($this->mo->ref, 1, 4);
1130 if ($this->mo->status > 0 && $ref == 'PROV') {
1131 throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
1132 }
1133
1134 if (strtolower($this->mo->ref) == 'auto') {
1135 if (empty($this->mo->id) && $this->mo->status == 0) {
1136 $this->mo->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
1137 } else {
1138 $this->mo->fetch_product();
1139 $numref = $this->mo->getNextNumRef($this->mo->product);
1140 $this->mo->ref = $numref;
1141 }
1142 }
1143 }
1144}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class to manage categories.
Class for API REST v1.
Definition api.class.php:35
_checkValExtrafieldsForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid')
Check access by user to a given resource.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Class for Mo.
Definition mo.class.php:35
Class MoLine.
produceAndConsume($id, $request_data=null)
Produce and consume.
__construct()
Constructor.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $sqlfilters='', $properties='')
List Mos.
confirmProduced($id, $notrigger=0)
Close=Confirm Produced MO.
put($id, $request_data=null)
Update MO.
getCategories($id, $sortfield="s.rowid", $sortorder='ASC', $limit=0, $page=0)
Get categories for a MO.
post($request_data=null)
Create MO object.
_cleanObjectDatas($object)
Clean sensible object datas @phpstan-template T.
produceAndConsumeAll($id, $request_data=null)
Produce and consume all.
validate($id, $notrigger=0)
Validate MO.
checkRefNumbering()
Validate the ref field and get the next Number if it's necessary.
_validate($data)
Validate fields before creating or updating an object.
Class to manage stock movements.
Class to manage products or services.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_now($mode='gmt')
Return date for now.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php