dolibarr 23.0.3
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';
24
25
38class Mos extends DolibarrApi
39{
43 public $mo;
44
48 public function __construct()
49 {
50 global $db, $conf;
51 $this->db = $db;
52 $this->mo = new Mo($this->db);
53 }
54
66 public function get($id)
67 {
68 if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
69 throw new RestException(403);
70 }
71
72 $result = $this->mo->fetch($id);
73 if (!$result) {
74 throw new RestException(404, 'MO not found');
75 }
76
77 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
78 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
79 }
80
81 return $this->_cleanObjectDatas($this->mo);
82 }
83
84
102 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
103 {
104 if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
105 throw new RestException(403);
106 }
107
108 $obj_ret = array();
109 $tmpobject = new Mo($this->db);
110
111 $socid = DolibarrApiAccess::$user->socid ?: 0;
112
113 $restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
114
115 // If the internal user must only see his customers, force searching by him
116 $search_sale = 0;
117 if ($restrictonsocid && !DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
118 $search_sale = DolibarrApiAccess::$user->id;
119 }
120
121 $sql = "SELECT t.rowid";
122 $sql .= " FROM ".MAIN_DB_PREFIX.$tmpobject->table_element." AS t";
123 $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
124 $sql .= " WHERE 1 = 1";
125 if ($tmpobject->ismultientitymanaged) {
126 $sql .= ' AND t.entity IN ('.getEntity($tmpobject->element).')';
127 }
128 if ($restrictonsocid && $socid) {
129 $sql .= " AND t.fk_soc = ".((int) $socid);
130 }
131 // Search on sale representative
132 if ($search_sale && $search_sale != '-1') {
133 if ($search_sale == -2) {
134 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
135 } elseif ($search_sale > 0) {
136 $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).")";
137 }
138 }
139 if ($sqlfilters) {
140 $errormessage = '';
141 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
142 if ($errormessage) {
143 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
144 }
145 }
146
147 $sql .= $this->db->order($sortfield, $sortorder);
148 if ($limit) {
149 if ($page < 0) {
150 $page = 0;
151 }
152 $offset = $limit * $page;
153
154 $sql .= $this->db->plimit($limit + 1, $offset);
155 }
156
157 $result = $this->db->query($sql);
158 if ($result) {
159 $i = 0;
160 $num = $this->db->num_rows($result);
161 $min = min($num, ($limit <= 0 ? $num : $limit));
162 while ($i < $min) {
163 $obj = $this->db->fetch_object($result);
164 $tmp_object = new Mo($this->db);
165 if ($tmp_object->fetch($obj->rowid)) {
166 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($tmp_object), $properties);
167 }
168 $i++;
169 }
170 } else {
171 throw new RestException(503, 'Error when retrieve MO list');
172 }
173
174 return $obj_ret;
175 }
176
187 public function post($request_data = null)
188 {
189 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
190 throw new RestException(403);
191 }
192 // Check mandatory fields
193 $result = $this->_validate($request_data);
194
195 foreach ($request_data as $field => $value) {
196 if ($field === 'caller') {
197 // 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
198 $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
199 continue;
200 }
201
202 $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
203 }
204
205 $this->checkRefNumbering();
206
207 $result = $this->mo->create(DolibarrApiAccess::$user);
208 //var_dump($result);exit;
209 if ($result < 0) {
210 throw new RestException(500, "Error creating MO", array_merge(array($this->mo->error), $this->mo->errors));
211 }
212
213 return $this->mo->id;
214 }
215
225 public function put($id, $request_data = null)
226 {
227 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
228 throw new RestException(403);
229 }
230
231 $result = $this->mo->fetch($id);
232 if (!$result) {
233 throw new RestException(404, 'MO not found');
234 }
235
236 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
237 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
238 }
239
240 foreach ($request_data as $field => $value) {
241 if ($field == 'id') {
242 continue;
243 }
244 if ($field === 'caller') {
245 // 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
246 $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
247 continue;
248 }
249
250 if ($field == 'array_options' && is_array($value)) {
251 foreach ($value as $index => $val) {
252 $this->mo->array_options[$index] = $this->_checkValForAPI($field, $val, $this->mo);
253 }
254 continue;
255 }
256
257 $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
258 }
259
260 $this->checkRefNumbering();
261
262 if ($this->mo->update(DolibarrApiAccess::$user) > 0) {
263 return $this->get($id);
264 } else {
265 throw new RestException(500, $this->mo->error);
266 }
267 }
268
283 public function validate($id, $notrigger = 0)
284 {
285 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
286 throw new RestException(403);
287 }
288
289 $result = $this->mo->fetch($id);
290 if (!$result) {
291 throw new RestException(404, 'MO not found');
292 }
293
294 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
295 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
296 }
297
298 $result = $this->mo->validate(DolibarrApiAccess::$user, $notrigger);
299 if ($result == 0) {
300 throw new RestException(304, 'Error nothing done. May be object is already validated');
301 }
302 if ($result < 0) {
303 throw new RestException(500, 'Error when validating MO: '.$this->mo->error);
304 }
305 $result = $this->mo->fetch($id);
306
307 return $this->_cleanObjectDatas($this->mo);
308 }
309
318 public function delete($id)
319 {
320 if (!DolibarrApiAccess::$user->hasRight('mrp', 'delete')) {
321 throw new RestException(403);
322 }
323 $result = $this->mo->fetch($id);
324 if (!$result) {
325 throw new RestException(404, 'MO not found');
326 }
327
328 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
329 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
330 }
331
332 if (!$this->mo->delete(DolibarrApiAccess::$user)) {
333 throw new RestException(500, 'Error when deleting MO : '.$this->mo->error);
334 }
335
336 return array(
337 'success' => array(
338 'code' => 200,
339 'message' => 'MO deleted'
340 )
341 );
342 }
343
344
377 public function produceAndConsumeAll($id, $request_data = null)
378 {
379 global $langs;
380
381 $error = 0;
382
383 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
384 throw new RestException(403, 'Not enough permission');
385 }
386 $result = $this->mo->fetch($id);
387 if (!$result) {
388 throw new RestException(404, 'MO not found');
389 }
390
391 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
392 throw new RestException(405, 'Error bad status of MO');
393 }
394
395 // Code for consume and produce...
396 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
397 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
398 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
399
400 $stockmove = new MouvementStock($this->db);
401
402 $labelmovement = '';
403 $codemovement = '';
404 $autoclose = 1;
405 $arraytoconsume = array();
406 $arraytoproduce = array();
407
408 foreach ($request_data as $field => $value) {
409 if ($field == 'inventorylabel') {
410 $labelmovement = $value;
411 }
412 if ($field == 'inventorycode') {
413 $codemovement = $value;
414 }
415 if ($field == 'autoclose') {
416 $autoclose = $value;
417 }
418 if ($field == 'arraytoconsume') {
419 $arraytoconsume = $value;
420 }
421 if ($field == 'arraytoproduce') {
422 $arraytoproduce = $value;
423 }
424 if ($field === 'caller') {
425 // 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
426 $stockmove->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
427 continue;
428 }
429 }
430
431 if (empty($labelmovement)) {
432 throw new RestException(500, "Field inventorylabel not provided");
433 }
434 if (empty($codemovement)) {
435 throw new RestException(500, "Field inventorycode not provided");
436 }
437
438 $consumptioncomplete = true;
439 $productioncomplete = true;
440
441 if (!empty($arraytoconsume) && !empty($arraytoproduce)) {
442 $pos = 0;
443 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
444 foreach ($arrayofarrayname as $arrayname) {
445 foreach (${$arrayname} as $value) {
446 $tmpproduct = new Product($this->db);
447 if (empty($value["objectid"])) {
448 throw new RestException(500, "Field objectid required in ".$arrayname);
449 }
450 $tmpproduct->fetch($value["qty"]);
451 if (empty($value["qty"])) {
452 throw new RestException(500, "Field qty required in ".$arrayname);
453 }
454 if ($value["qty"] != 0) {
455 $qtytoprocess = $value["qty"];
456 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
457 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
458 $error++;
459 throw new RestException(500, "Field fk_warehouse must be > 0 in ".$arrayname);
460 }
461 if ($tmpproduct->status_batch) {
462 $error++;
463 throw new RestException(500, "Product ".$tmpproduct->ref."must be in batch");
464 }
465 }
466 $idstockmove = 0;
467 if (!$error && $value["fk_warehouse"] > 0) {
468 // Record consumption to do and stock movement
469 $id_product_batch = 0;
470
471 $stockmove->setOrigin($this->mo->element, $this->mo->id);
472
473 if ($arrayname == 'arraytoconsume') {
474 $moline = new MoLine($this->db);
475 $moline->fk_mo = $this->mo->id;
476 $moline->position = $pos;
477 $moline->fk_product = $value["objectid"];
478 $moline->fk_warehouse = (int) $value["fk_warehouse"];
479 $moline->qty = $qtytoprocess;
480 $moline->batch = (string) $tmpproduct->status_batch;
481 $moline->role = 'toproduce';
482 $moline->fk_mrp_production = 0;
483 $moline->fk_stock_movement = $idstockmove;
484 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
485
486 $resultmoline = $moline->create(DolibarrApiAccess::$user);
487 if ($resultmoline <= 0) {
488 $error++;
489 throw new RestException(500, $moline->error);
490 }
491 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
492 } else {
493 $moline = new MoLine($this->db);
494 $moline->fk_mo = $this->mo->id;
495 $moline->position = $pos;
496 $moline->fk_product = $value["objectid"];
497 $moline->fk_warehouse = $value["fk_warehouse"];
498 $moline->qty = $qtytoprocess;
499 $moline->batch = (string) $tmpproduct->status_batch;
500 $moline->role = 'toconsume';
501 $moline->fk_mrp_production = 0;
502 $moline->fk_stock_movement = $idstockmove;
503 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
504
505 $resultmoline = $moline->create(DolibarrApiAccess::$user);
506 if ($resultmoline <= 0) {
507 $error++;
508 throw new RestException(500, $moline->error);
509 }
510 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
511 }
512 if ($idstockmove < 0) {
513 $error++;
514 throw new RestException(500, $stockmove->error);
515 }
516 }
517 if (!$error) {
518 // Record consumption done
519 $moline = new MoLine($this->db);
520 $moline->fk_mo = $this->mo->id;
521 $moline->position = $pos;
522 $moline->fk_product = $value["objectid"];
523 $moline->fk_warehouse = $value["fk_warehouse"];
524 $moline->qty = $qtytoprocess;
525 $moline->batch = (string) $tmpproduct->status_batch;
526 if ($arrayname == "arraytoconsume") {
527 $moline->role = 'consumed';
528 } else {
529 $moline->role = 'produced';
530 }
531 $moline->fk_mrp_production = 0;
532 $moline->fk_stock_movement = $idstockmove;
533 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
534
535 $resultmoline = $moline->create(DolibarrApiAccess::$user);
536 if ($resultmoline <= 0) {
537 $error++;
538 throw new RestException(500, $moline->error);
539 }
540
541 $pos++;
542 }
543 }
544 }
545 }
546 if (!$error) {
547 if ($autoclose <= 0) {
548 $consumptioncomplete = false;
549 $productioncomplete = false;
550 }
551 }
552 } else {
553 $pos = 0;
554 foreach ($this->mo->lines as $line) {
555 if ($line->role == 'toconsume') {
556 $tmpproduct = new Product($this->db);
557 $tmpproduct->fetch($line->fk_product);
558 if ($line->qty != 0) {
559 $qtytoprocess = $line->qty;
560 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
561 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
562 $langs->load("errors");
563 $error++;
564 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
565 }
566 if ($tmpproduct->status_batch) {
567 $langs->load("errors");
568 $error++;
569 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
570 }
571 }
572 $idstockmove = 0;
573 if (!$error && $line->fk_warehouse > 0) {
574 // Record stock movement
575 $id_product_batch = 0;
576 $stockmove->origin_type = 'mo';
577 $stockmove->origin_id = $this->mo->id;
578 if ($qtytoprocess >= 0) {
579 $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);
580 } else {
581 $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);
582 }
583 if ($idstockmove < 0) {
584 $error++;
585 throw new RestException(500, $stockmove->error);
586 }
587 }
588 if (!$error) {
589 // Record consumption
590 $moline = new MoLine($this->db);
591 $moline->fk_mo = $this->mo->id;
592 $moline->position = $pos;
593 $moline->fk_product = $line->fk_product;
594 $moline->fk_warehouse = $line->fk_warehouse;
595 $moline->qty = $qtytoprocess;
596 $moline->batch = (string) $tmpproduct->status_batch;
597 $moline->role = 'consumed';
598 $moline->fk_mrp_production = $line->id;
599 $moline->fk_stock_movement = $idstockmove;
600 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
601
602 $resultmoline = $moline->create(DolibarrApiAccess::$user);
603 if ($resultmoline <= 0) {
604 $error++;
605 throw new RestException(500, $moline->error);
606 }
607
608 $pos++;
609 }
610 }
611 }
612 }
613 $pos = 0;
614 foreach ($this->mo->lines as $line) {
615 if ($line->role == 'toproduce') {
616 $tmpproduct = new Product($this->db);
617 $tmpproduct->fetch($line->fk_product);
618 if ($line->qty != 0) {
619 $qtytoprocess = $line->qty;
620 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
621 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
622 $langs->load("errors");
623 $error++;
624 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
625 }
626 if ($tmpproduct->status_batch) {
627 $langs->load("errors");
628 $error++;
629 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
630 }
631 }
632 $idstockmove = 0;
633 if (!$error && $line->fk_warehouse > 0) {
634 // Record stock movement
635 $id_product_batch = 0;
636 $stockmove->origin_type = 'mo';
637 $stockmove->origin_id = $this->mo->id;
638 if ($qtytoprocess >= 0) {
639 $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);
640 } else {
641 $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);
642 }
643 if ($idstockmove < 0) {
644 $error++;
645 throw new RestException(500, $stockmove->error);
646 }
647 }
648 if (!$error) {
649 // Record consumption
650 $moline = new MoLine($this->db);
651 $moline->fk_mo = $this->mo->id;
652 $moline->position = $pos;
653 $moline->fk_product = $line->fk_product;
654 $moline->fk_warehouse = $line->fk_warehouse;
655 $moline->qty = $qtytoprocess;
656 $moline->batch = (string) $tmpproduct->status_batch;
657 $moline->role = 'produced';
658 $moline->fk_mrp_production = $line->id;
659 $moline->fk_stock_movement = $idstockmove;
660 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
661
662 $resultmoline = $moline->create(DolibarrApiAccess::$user);
663 if ($resultmoline <= 0) {
664 $error++;
665 throw new RestException(500, $moline->error);
666 }
667
668 $pos++;
669 }
670 }
671 }
672 }
673
674 if (!$error) {
675 if ($autoclose > 0) {
676 foreach ($this->mo->lines as $line) {
677 if ($line->role == 'toconsume') {
678 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
679 $alreadyconsumed = 0;
680 foreach ($arrayoflines as $line2) {
681 $alreadyconsumed += $line2['qty'];
682 }
683
684 if ($alreadyconsumed < $line->qty) {
685 $consumptioncomplete = false;
686 }
687 }
688 if ($line->role == 'toproduce') {
689 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
690 $alreadyproduced = 0;
691 foreach ($arrayoflines as $line2) {
692 $alreadyproduced += $line2['qty'];
693 }
694
695 if ($alreadyproduced < $line->qty) {
696 $productioncomplete = false;
697 }
698 }
699 }
700 } else {
701 $consumptioncomplete = false;
702 $productioncomplete = false;
703 }
704 }
705 }
706
707 // Update status of MO
708 dol_syslog("consumptioncomplete = ".json_encode($consumptioncomplete)." productioncomplete = ".json_encode($productioncomplete));
709 if ($consumptioncomplete && $productioncomplete) {
710 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
711 } else {
712 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
713 }
714 if ($result <= 0) {
715 throw new RestException(500, $this->mo->error);
716 }
717
718 return $this->mo->id;
719 }
720
755 public function produceAndConsume($id, $request_data = null)
756 {
757 if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
758 throw new RestException(403, 'Not enough permission');
759 }
760 $result = $this->mo->fetch($id);
761 if (!$result) {
762 throw new RestException(404, 'MO not found');
763 }
764
765 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
766 throw new RestException(405, 'Error bad status of MO');
767 }
768
769 // Code for consume and produce...
770 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
771 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
772 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
773
774 $stockmove = new MouvementStock($this->db);
775
776 $labelmovement = '';
777 $codemovement = '';
778 $autoclose = 1;
779 $arraytoconsume = array();
780 $arraytoproduce = array();
781
782 foreach ($request_data as $field => $value) {
783 if ($field == 'inventorylabel') {
784 $labelmovement = $value;
785 }
786 if ($field == 'inventorycode') {
787 $codemovement = $value;
788 }
789 if ($field == 'autoclose') {
790 $autoclose = $value;
791 }
792 if ($field == 'arraytoconsume') {
793 $arraytoconsume = $value;
794 }
795 if ($field == 'arraytoproduce') {
796 $arraytoproduce = $value;
797 }
798 if ($field === 'caller') {
799 // 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
800 $stockmove->context['caller'] = $request_data['caller'];
801 continue;
802 }
803 }
804
805 if (empty($labelmovement)) {
806 throw new RestException(500, "Field inventorylabel not provided");
807 }
808 if (empty($codemovement)) {
809 throw new RestException(500, "Field inventorycode not provided");
810 }
811
812 $this->db->begin();
813
814 $pos = 0;
815 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
816 foreach ($arrayofarrayname as $arrayname) {
817 foreach (${$arrayname} as $value) {
818 if (empty($value["objectid"])) {
819 throw new RestException(500, "Field objectid required in " . $arrayname);
820 }
821
822 $molinetoprocess = new MoLine($this->db);
823 $tmpmolineid = $molinetoprocess->fetch($value["objectid"]);
824 if ($tmpmolineid <= 0) {
825 throw new RestException(500, "MoLine with rowid " . $value["objectid"] . " not exist.");
826 }
827
828 $tmpproduct = new Product($this->db);
829 $tmpproduct->fetch($molinetoprocess->fk_product);
830 if ($tmpproduct->status_batch) {
831 throw new RestException(500, "Product " . $tmpproduct->ref . " must be in batch, this API can't handle it currently.");
832 }
833
834 if (empty($value["qty"]) && $value["qty"] != 0) {
835 throw new RestException(500, "Field qty with lower or higher then 0 required in " . $arrayname);
836 }
837 $qtytoprocess = $value["qty"];
838
839 $fk_warehousetoprocess = 0;
840 if ($molinetoprocess->disable_stock_change == false) {
841 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
842 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
843 throw new RestException(500, "Field fk_warehouse required in " . $arrayname);
844 }
845 }
846 $fk_warehousetoprocess = (int) $value["fk_warehouse"];
847 }
848
849 $pricetoproduce = 0;
850 if (isset($value["pricetoproduce"])) { // If there is a price to produce set.
851 if ($value["pricetoproduce"] > 0) { // Only use prices grater then 0.
852 $pricetoproduce = $value["pricetoproduce"];
853 }
854 }
855
856 $idstockmove = 0;
857
858 if ($molinetoprocess->disable_stock_change == false) {
859 // Record stock movement
860 $id_product_batch = 0;
861 $stockmove->origin_type = 'mo';
862 $stockmove->origin_id = $this->mo->id;
863 if ($arrayname == "arraytoconsume") {
864 if ($qtytoprocess >= 0) {
865 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
866 } else {
867 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
868 }
869 } else {
870 if ($qtytoprocess >= 0) {
871 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, $pricetoproduce, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
872 } else {
873 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
874 }
875 }
876 if ($idstockmove <= 0) {
877 throw new RestException(500, $stockmove->error);
878 }
879 }
880
881 // Record consumption
882 $moline = new MoLine($this->db);
883 $moline->fk_mo = $this->mo->id;
884 $moline->position = $pos;
885 $moline->fk_product = $tmpproduct->id;
886 $moline->fk_warehouse = $idstockmove > 0 ? $fk_warehousetoprocess : null;
887 $moline->qty = $qtytoprocess;
888 $moline->batch = '';
889 $moline->fk_mrp_production = $molinetoprocess->id;
890 $moline->fk_stock_movement = $idstockmove > 0 ? $idstockmove : null;
891 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
892
893 if ($arrayname == "arraytoconsume") {
894 $moline->role = 'consumed';
895 } else {
896 $moline->role = 'produced';
897 }
898
899 $resultmoline = $moline->create(DolibarrApiAccess::$user);
900 if ($resultmoline <= 0) {
901 throw new RestException(500, $moline->error);
902 }
903
904 $pos++;
905 }
906 }
907
908 $consumptioncomplete = true;
909 $productioncomplete = true;
910
911 if ($autoclose > 0) {
912 // Refresh Lines after consumptions.
913 $this->mo->fetchLines();
914
915 foreach ($this->mo->lines as $line) {
916 if ($line->role == 'toconsume') {
917 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
918 $alreadyconsumed = 0;
919 foreach ($arrayoflines as $line2) {
920 $alreadyconsumed += $line2['qty'];
921 }
922
923 if ($alreadyconsumed < $line->qty) {
924 $consumptioncomplete = false;
925 }
926 }
927 if ($line->role == 'toproduce') {
928 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
929 $alreadyproduced = 0;
930 foreach ($arrayoflines as $line2) {
931 $alreadyproduced += $line2['qty'];
932 }
933
934 if ($alreadyproduced < $line->qty) {
935 $productioncomplete = false;
936 }
937 }
938 }
939 } else {
940 $consumptioncomplete = false;
941 $productioncomplete = false;
942 }
943
944 // Update status of MO
945 dol_syslog("consumptioncomplete = " . (string) $consumptioncomplete . " productioncomplete = " . (string) $productioncomplete);
946 //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
947 if ($consumptioncomplete && $productioncomplete) {
948 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
949 } else {
950 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
951 }
952 if ($result <= 0) {
953 throw new RestException(500, $this->mo->error);
954 }
955
956 $this->db->commit();
957 return $this->mo->id;
958 }
959
960
961 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
971 protected function _cleanObjectDatas($object)
972 {
973 // phpcs:enable
974 $object = parent::_cleanObjectDatas($object);
975
976 unset($object->rowid);
977 unset($object->canvas);
978
979 unset($object->name);
980 unset($object->lastname);
981 unset($object->firstname);
982 unset($object->civility_id);
983 unset($object->statut);
984 unset($object->state);
985 unset($object->state_id);
986 unset($object->state_code);
987 unset($object->region);
988 unset($object->region_code);
989 unset($object->country);
990 unset($object->country_id);
991 unset($object->country_code);
992 unset($object->barcode_type);
993 unset($object->barcode_type_code);
994 unset($object->barcode_type_label);
995 unset($object->barcode_type_coder);
996 unset($object->total_ht);
997 unset($object->total_tva);
998 unset($object->total_localtax1);
999 unset($object->total_localtax2);
1000 unset($object->total_ttc);
1001 unset($object->fk_account);
1002 unset($object->comments);
1003 unset($object->note);
1004 unset($object->mode_reglement_id);
1005 unset($object->cond_reglement_id);
1006 unset($object->cond_reglement);
1007 unset($object->shipping_method_id);
1008 unset($object->fk_incoterms);
1009 unset($object->label_incoterms);
1010 unset($object->location_incoterms);
1011
1012 // If object has lines, remove $db property
1013 if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
1014 $nboflines = count($object->lines);
1015 for ($i = 0; $i < $nboflines; $i++) {
1016 $this->_cleanObjectDatas($object->lines[$i]);
1017
1018 unset($object->lines[$i]->lines);
1019 unset($object->lines[$i]->note);
1020 }
1021 }
1022
1023 return $object;
1024 }
1025
1034 private function _validate($data)
1035 {
1036 $myobject = array();
1037 foreach ($this->mo->fields as $field => $propfield) {
1038 if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || empty($propfield['notnull']) || $propfield['notnull'] != 1) {
1039 continue; // Not a mandatory field
1040 }
1041 if (!isset($data[$field])) {
1042 throw new RestException(400, "$field field missing");
1043 }
1044 $myobject[$field] = $data[$field];
1045 }
1046 return $myobject;
1047 }
1048
1054 private function checkRefNumbering()
1055 {
1056 $ref = substr($this->mo->ref, 1, 4);
1057 if ($this->mo->status > 0 && $ref == 'PROV') {
1058 throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
1059 }
1060
1061 if (strtolower($this->mo->ref) == 'auto') {
1062 if (empty($this->mo->id) && $this->mo->status == 0) {
1063 $this->mo->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
1064 } else {
1065 $this->mo->fetch_product();
1066 $numref = $this->mo->getNextNumRef($this->mo->product);
1067 $this->mo->ref = $numref;
1068 }
1069 }
1070 }
1071}
$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 for API REST v1.
Definition api.class.php:33
_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.
Definition api.class.php:98
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.
put($id, $request_data=null)
Update 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.
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.