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
277 public function delete($id)
278 {
279 if (!DolibarrApiAccess::$user->hasRight('mrp', 'delete')) {
280 throw new RestException(403);
281 }
282 $result = $this->mo->fetch($id);
283 if (!$result) {
284 throw new RestException(404, 'MO not found');
285 }
286
287 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
288 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
289 }
290
291 if (!$this->mo->delete(DolibarrApiAccess::$user)) {
292 throw new RestException(500, 'Error when deleting MO : '.$this->mo->error);
293 }
294
295 return array(
296 'success' => array(
297 'code' => 200,
298 'message' => 'MO deleted'
299 )
300 );
301 }
302
303
336 public function produceAndConsumeAll($id, $request_data = null)
337 {
338 global $langs;
339
340 $error = 0;
341
342 if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
343 throw new RestException(403, 'Not enough permission');
344 }
345 $result = $this->mo->fetch($id);
346 if (!$result) {
347 throw new RestException(404, 'MO not found');
348 }
349
350 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
351 throw new RestException(405, 'Error bad status of MO');
352 }
353
354 // Code for consume and produce...
355 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
356 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
357 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
358
359 $stockmove = new MouvementStock($this->db);
360
361 $labelmovement = '';
362 $codemovement = '';
363 $autoclose = 1;
364 $arraytoconsume = array();
365 $arraytoproduce = array();
366
367 foreach ($request_data as $field => $value) {
368 if ($field == 'inventorylabel') {
369 $labelmovement = $value;
370 }
371 if ($field == 'inventorycode') {
372 $codemovement = $value;
373 }
374 if ($field == 'autoclose') {
375 $autoclose = $value;
376 }
377 if ($field == 'arraytoconsume') {
378 $arraytoconsume = $value;
379 }
380 if ($field == 'arraytoproduce') {
381 $arraytoproduce = $value;
382 }
383 if ($field === 'caller') {
384 // 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
385 $stockmove->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
386 continue;
387 }
388 }
389
390 if (empty($labelmovement)) {
391 throw new RestException(500, "Field inventorylabel not provided");
392 }
393 if (empty($codemovement)) {
394 throw new RestException(500, "Field inventorycode not provided");
395 }
396
397 $consumptioncomplete = true;
398 $productioncomplete = true;
399
400 if (!empty($arraytoconsume) && !empty($arraytoproduce)) {
401 $pos = 0;
402 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
403 foreach ($arrayofarrayname as $arrayname) {
404 foreach (${$arrayname} as $value) {
405 $tmpproduct = new Product($this->db);
406 if (empty($value["objectid"])) {
407 throw new RestException(500, "Field objectid required in ".$arrayname);
408 }
409 $tmpproduct->fetch($value["qty"]);
410 if (empty($value["qty"])) {
411 throw new RestException(500, "Field qty required in ".$arrayname);
412 }
413 if ($value["qty"] != 0) {
414 $qtytoprocess = $value["qty"];
415 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
416 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
417 $error++;
418 throw new RestException(500, "Field fk_warehouse must be > 0 in ".$arrayname);
419 }
420 if ($tmpproduct->status_batch) {
421 $error++;
422 throw new RestException(500, "Product ".$tmpproduct->ref."must be in batch");
423 }
424 }
425 $idstockmove = 0;
426 if (!$error && $value["fk_warehouse"] > 0) {
427 // Record consumption to do and stock movement
428 $id_product_batch = 0;
429
430 $stockmove->setOrigin($this->mo->element, $this->mo->id);
431
432 if ($arrayname == 'arraytoconsume') {
433 $moline = new MoLine($this->db);
434 $moline->fk_mo = $this->mo->id;
435 $moline->position = $pos;
436 $moline->fk_product = $value["objectid"];
437 $moline->fk_warehouse = (int) $value["fk_warehouse"];
438 $moline->qty = $qtytoprocess;
439 $moline->batch = (string) $tmpproduct->status_batch;
440 $moline->role = 'toproduce';
441 $moline->fk_mrp_production = 0;
442 $moline->fk_stock_movement = $idstockmove;
443 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
444
445 $resultmoline = $moline->create(DolibarrApiAccess::$user);
446 if ($resultmoline <= 0) {
447 $error++;
448 throw new RestException(500, $moline->error);
449 }
450 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
451 } else {
452 $moline = new MoLine($this->db);
453 $moline->fk_mo = $this->mo->id;
454 $moline->position = $pos;
455 $moline->fk_product = $value["objectid"];
456 $moline->fk_warehouse = $value["fk_warehouse"];
457 $moline->qty = $qtytoprocess;
458 $moline->batch = (string) $tmpproduct->status_batch;
459 $moline->role = 'toconsume';
460 $moline->fk_mrp_production = 0;
461 $moline->fk_stock_movement = $idstockmove;
462 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
463
464 $resultmoline = $moline->create(DolibarrApiAccess::$user);
465 if ($resultmoline <= 0) {
466 $error++;
467 throw new RestException(500, $moline->error);
468 }
469 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
470 }
471 if ($idstockmove < 0) {
472 $error++;
473 throw new RestException(500, $stockmove->error);
474 }
475 }
476 if (!$error) {
477 // Record consumption done
478 $moline = new MoLine($this->db);
479 $moline->fk_mo = $this->mo->id;
480 $moline->position = $pos;
481 $moline->fk_product = $value["objectid"];
482 $moline->fk_warehouse = $value["fk_warehouse"];
483 $moline->qty = $qtytoprocess;
484 $moline->batch = (string) $tmpproduct->status_batch;
485 if ($arrayname == "arraytoconsume") {
486 $moline->role = 'consumed';
487 } else {
488 $moline->role = 'produced';
489 }
490 $moline->fk_mrp_production = 0;
491 $moline->fk_stock_movement = $idstockmove;
492 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
493
494 $resultmoline = $moline->create(DolibarrApiAccess::$user);
495 if ($resultmoline <= 0) {
496 $error++;
497 throw new RestException(500, $moline->error);
498 }
499
500 $pos++;
501 }
502 }
503 }
504 }
505 if (!$error) {
506 if ($autoclose <= 0) {
507 $consumptioncomplete = false;
508 $productioncomplete = false;
509 }
510 }
511 } else {
512 $pos = 0;
513 foreach ($this->mo->lines as $line) {
514 if ($line->role == 'toconsume') {
515 $tmpproduct = new Product($this->db);
516 $tmpproduct->fetch($line->fk_product);
517 if ($line->qty != 0) {
518 $qtytoprocess = $line->qty;
519 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
520 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
521 $langs->load("errors");
522 $error++;
523 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
524 }
525 if ($tmpproduct->status_batch) {
526 $langs->load("errors");
527 $error++;
528 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
529 }
530 }
531 $idstockmove = 0;
532 if (!$error && $line->fk_warehouse > 0) {
533 // Record stock movement
534 $id_product_batch = 0;
535 $stockmove->origin_type = 'mo';
536 $stockmove->origin_id = $this->mo->id;
537 if ($qtytoprocess >= 0) {
538 $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);
539 } else {
540 $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);
541 }
542 if ($idstockmove < 0) {
543 $error++;
544 throw new RestException(500, $stockmove->error);
545 }
546 }
547 if (!$error) {
548 // Record consumption
549 $moline = new MoLine($this->db);
550 $moline->fk_mo = $this->mo->id;
551 $moline->position = $pos;
552 $moline->fk_product = $line->fk_product;
553 $moline->fk_warehouse = $line->fk_warehouse;
554 $moline->qty = $qtytoprocess;
555 $moline->batch = (string) $tmpproduct->status_batch;
556 $moline->role = 'consumed';
557 $moline->fk_mrp_production = $line->id;
558 $moline->fk_stock_movement = $idstockmove;
559 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
560
561 $resultmoline = $moline->create(DolibarrApiAccess::$user);
562 if ($resultmoline <= 0) {
563 $error++;
564 throw new RestException(500, $moline->error);
565 }
566
567 $pos++;
568 }
569 }
570 }
571 }
572 $pos = 0;
573 foreach ($this->mo->lines as $line) {
574 if ($line->role == 'toproduce') {
575 $tmpproduct = new Product($this->db);
576 $tmpproduct->fetch($line->fk_product);
577 if ($line->qty != 0) {
578 $qtytoprocess = $line->qty;
579 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
580 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
581 $langs->load("errors");
582 $error++;
583 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
584 }
585 if ($tmpproduct->status_batch) {
586 $langs->load("errors");
587 $error++;
588 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
589 }
590 }
591 $idstockmove = 0;
592 if (!$error && $line->fk_warehouse > 0) {
593 // Record stock movement
594 $id_product_batch = 0;
595 $stockmove->origin_type = 'mo';
596 $stockmove->origin_id = $this->mo->id;
597 if ($qtytoprocess >= 0) {
598 $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);
599 } else {
600 $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);
601 }
602 if ($idstockmove < 0) {
603 $error++;
604 throw new RestException(500, $stockmove->error);
605 }
606 }
607 if (!$error) {
608 // Record consumption
609 $moline = new MoLine($this->db);
610 $moline->fk_mo = $this->mo->id;
611 $moline->position = $pos;
612 $moline->fk_product = $line->fk_product;
613 $moline->fk_warehouse = $line->fk_warehouse;
614 $moline->qty = $qtytoprocess;
615 $moline->batch = (string) $tmpproduct->status_batch;
616 $moline->role = 'produced';
617 $moline->fk_mrp_production = $line->id;
618 $moline->fk_stock_movement = $idstockmove;
619 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
620
621 $resultmoline = $moline->create(DolibarrApiAccess::$user);
622 if ($resultmoline <= 0) {
623 $error++;
624 throw new RestException(500, $moline->error);
625 }
626
627 $pos++;
628 }
629 }
630 }
631 }
632
633 if (!$error) {
634 if ($autoclose > 0) {
635 foreach ($this->mo->lines as $line) {
636 if ($line->role == 'toconsume') {
637 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
638 $alreadyconsumed = 0;
639 foreach ($arrayoflines as $line2) {
640 $alreadyconsumed += $line2['qty'];
641 }
642
643 if ($alreadyconsumed < $line->qty) {
644 $consumptioncomplete = false;
645 }
646 }
647 if ($line->role == 'toproduce') {
648 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
649 $alreadyproduced = 0;
650 foreach ($arrayoflines as $line2) {
651 $alreadyproduced += $line2['qty'];
652 }
653
654 if ($alreadyproduced < $line->qty) {
655 $productioncomplete = false;
656 }
657 }
658 }
659 } else {
660 $consumptioncomplete = false;
661 $productioncomplete = false;
662 }
663 }
664 }
665
666 // Update status of MO
667 dol_syslog("consumptioncomplete = ".json_encode($consumptioncomplete)." productioncomplete = ".json_encode($productioncomplete));
668 if ($consumptioncomplete && $productioncomplete) {
669 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
670 } else {
671 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
672 }
673 if ($result <= 0) {
674 throw new RestException(500, $this->mo->error);
675 }
676
677 return $this->mo->id;
678 }
679
714 public function produceAndConsume($id, $request_data = null)
715 {
716 if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
717 throw new RestException(403, 'Not enough permission');
718 }
719 $result = $this->mo->fetch($id);
720 if (!$result) {
721 throw new RestException(404, 'MO not found');
722 }
723
724 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
725 throw new RestException(405, 'Error bad status of MO');
726 }
727
728 // Code for consume and produce...
729 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
730 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
731 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
732
733 $stockmove = new MouvementStock($this->db);
734
735 $labelmovement = '';
736 $codemovement = '';
737 $autoclose = 1;
738 $arraytoconsume = array();
739 $arraytoproduce = array();
740
741 foreach ($request_data as $field => $value) {
742 if ($field == 'inventorylabel') {
743 $labelmovement = $value;
744 }
745 if ($field == 'inventorycode') {
746 $codemovement = $value;
747 }
748 if ($field == 'autoclose') {
749 $autoclose = $value;
750 }
751 if ($field == 'arraytoconsume') {
752 $arraytoconsume = $value;
753 }
754 if ($field == 'arraytoproduce') {
755 $arraytoproduce = $value;
756 }
757 if ($field === 'caller') {
758 // 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
759 $stockmove->context['caller'] = $request_data['caller'];
760 continue;
761 }
762 }
763
764 if (empty($labelmovement)) {
765 throw new RestException(500, "Field inventorylabel not provided");
766 }
767 if (empty($codemovement)) {
768 throw new RestException(500, "Field inventorycode not provided");
769 }
770
771 $this->db->begin();
772
773 $pos = 0;
774 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
775 foreach ($arrayofarrayname as $arrayname) {
776 foreach (${$arrayname} as $value) {
777 if (empty($value["objectid"])) {
778 throw new RestException(500, "Field objectid required in " . $arrayname);
779 }
780
781 $molinetoprocess = new MoLine($this->db);
782 $tmpmolineid = $molinetoprocess->fetch($value["objectid"]);
783 if ($tmpmolineid <= 0) {
784 throw new RestException(500, "MoLine with rowid " . $value["objectid"] . " not exist.");
785 }
786
787 $tmpproduct = new Product($this->db);
788 $tmpproduct->fetch($molinetoprocess->fk_product);
789 if ($tmpproduct->status_batch) {
790 throw new RestException(500, "Product " . $tmpproduct->ref . " must be in batch, this API can't handle it currently.");
791 }
792
793 if (empty($value["qty"]) && $value["qty"] != 0) {
794 throw new RestException(500, "Field qty with lower or higher then 0 required in " . $arrayname);
795 }
796 $qtytoprocess = $value["qty"];
797
798 $fk_warehousetoprocess = 0;
799 if ($molinetoprocess->disable_stock_change == false) {
800 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
801 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
802 throw new RestException(500, "Field fk_warehouse required in " . $arrayname);
803 }
804 }
805 $fk_warehousetoprocess = (int) $value["fk_warehouse"];
806 }
807
808 $pricetoproduce = 0;
809 if (isset($value["pricetoproduce"])) { // If there is a price to produce set.
810 if ($value["pricetoproduce"] > 0) { // Only use prices grater then 0.
811 $pricetoproduce = $value["pricetoproduce"];
812 }
813 }
814
815 $idstockmove = 0;
816
817 if ($molinetoprocess->disable_stock_change == false) {
818 // Record stock movement
819 $id_product_batch = 0;
820 $stockmove->origin_type = 'mo';
821 $stockmove->origin_id = $this->mo->id;
822 if ($arrayname == "arraytoconsume") {
823 if ($qtytoprocess >= 0) {
824 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
825 } else {
826 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
827 }
828 } else {
829 if ($qtytoprocess >= 0) {
830 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, $pricetoproduce, $labelmovement, '', '', (string) $tmpproduct->status_batch, dol_now(), $id_product_batch, $codemovement);
831 } else {
832 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', (string) $tmpproduct->status_batch, $id_product_batch, $codemovement);
833 }
834 }
835 if ($idstockmove <= 0) {
836 throw new RestException(500, $stockmove->error);
837 }
838 }
839
840 // Record consumption
841 $moline = new MoLine($this->db);
842 $moline->fk_mo = $this->mo->id;
843 $moline->position = $pos;
844 $moline->fk_product = $tmpproduct->id;
845 $moline->fk_warehouse = $idstockmove > 0 ? $fk_warehousetoprocess : null;
846 $moline->qty = $qtytoprocess;
847 $moline->batch = '';
848 $moline->fk_mrp_production = $molinetoprocess->id;
849 $moline->fk_stock_movement = $idstockmove > 0 ? $idstockmove : null;
850 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
851
852 if ($arrayname == "arraytoconsume") {
853 $moline->role = 'consumed';
854 } else {
855 $moline->role = 'produced';
856 }
857
858 $resultmoline = $moline->create(DolibarrApiAccess::$user);
859 if ($resultmoline <= 0) {
860 throw new RestException(500, $moline->error);
861 }
862
863 $pos++;
864 }
865 }
866
867 $consumptioncomplete = true;
868 $productioncomplete = true;
869
870 if ($autoclose > 0) {
871 // Refresh Lines after consumptions.
872 $this->mo->fetchLines();
873
874 foreach ($this->mo->lines as $line) {
875 if ($line->role == 'toconsume') {
876 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
877 $alreadyconsumed = 0;
878 foreach ($arrayoflines as $line2) {
879 $alreadyconsumed += $line2['qty'];
880 }
881
882 if ($alreadyconsumed < $line->qty) {
883 $consumptioncomplete = false;
884 }
885 }
886 if ($line->role == 'toproduce') {
887 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
888 $alreadyproduced = 0;
889 foreach ($arrayoflines as $line2) {
890 $alreadyproduced += $line2['qty'];
891 }
892
893 if ($alreadyproduced < $line->qty) {
894 $productioncomplete = false;
895 }
896 }
897 }
898 } else {
899 $consumptioncomplete = false;
900 $productioncomplete = false;
901 }
902
903 // Update status of MO
904 dol_syslog("consumptioncomplete = " . (string) $consumptioncomplete . " productioncomplete = " . (string) $productioncomplete);
905 //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
906 if ($consumptioncomplete && $productioncomplete) {
907 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
908 } else {
909 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
910 }
911 if ($result <= 0) {
912 throw new RestException(500, $this->mo->error);
913 }
914
915 $this->db->commit();
916 return $this->mo->id;
917 }
918
919
920 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
930 protected function _cleanObjectDatas($object)
931 {
932 // phpcs:enable
933 $object = parent::_cleanObjectDatas($object);
934
935 unset($object->rowid);
936 unset($object->canvas);
937
938 unset($object->name);
939 unset($object->lastname);
940 unset($object->firstname);
941 unset($object->civility_id);
942 unset($object->statut);
943 unset($object->state);
944 unset($object->state_id);
945 unset($object->state_code);
946 unset($object->region);
947 unset($object->region_code);
948 unset($object->country);
949 unset($object->country_id);
950 unset($object->country_code);
951 unset($object->barcode_type);
952 unset($object->barcode_type_code);
953 unset($object->barcode_type_label);
954 unset($object->barcode_type_coder);
955 unset($object->total_ht);
956 unset($object->total_tva);
957 unset($object->total_localtax1);
958 unset($object->total_localtax2);
959 unset($object->total_ttc);
960 unset($object->fk_account);
961 unset($object->comments);
962 unset($object->note);
963 unset($object->mode_reglement_id);
964 unset($object->cond_reglement_id);
965 unset($object->cond_reglement);
966 unset($object->shipping_method_id);
967 unset($object->fk_incoterms);
968 unset($object->label_incoterms);
969 unset($object->location_incoterms);
970
971 // If object has lines, remove $db property
972 if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
973 $nboflines = count($object->lines);
974 for ($i = 0; $i < $nboflines; $i++) {
975 $this->_cleanObjectDatas($object->lines[$i]);
976
977 unset($object->lines[$i]->lines);
978 unset($object->lines[$i]->note);
979 }
980 }
981
982 return $object;
983 }
984
993 private function _validate($data)
994 {
995 $myobject = array();
996 foreach ($this->mo->fields as $field => $propfield) {
997 if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || empty($propfield['notnull']) || $propfield['notnull'] != 1) {
998 continue; // Not a mandatory field
999 }
1000 if (!isset($data[$field])) {
1001 throw new RestException(400, "$field field missing");
1002 }
1003 $myobject[$field] = $data[$field];
1004 }
1005 return $myobject;
1006 }
1007
1013 private function checkRefNumbering()
1014 {
1015 $ref = substr($this->mo->ref, 1, 4);
1016 if ($this->mo->status > 0 && $ref == 'PROV') {
1017 throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
1018 }
1019
1020 if (strtolower($this->mo->ref) == 'auto') {
1021 if (empty($this->mo->id) && $this->mo->status == 0) {
1022 $this->mo->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
1023 } else {
1024 $this->mo->fetch_product();
1025 $numref = $this->mo->getNextNumRef($this->mo->product);
1026 $this->mo->ref = $numref;
1027 }
1028 }
1029 }
1030}
$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.
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.