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