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