dolibarr 19.0.4
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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use Luracast\Restler\RestException;
20
21require_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
22
23
36class Mos extends DolibarrApi
37{
41 public $mo;
42
46 public function __construct()
47 {
48 global $db, $conf;
49 $this->db = $db;
50 $this->mo = new Mo($this->db);
51 }
52
64 public function get($id)
65 {
66 if (!DolibarrApiAccess::$user->rights->mrp->read) {
67 throw new RestException(401);
68 }
69
70 $result = $this->mo->fetch($id);
71 if (!$result) {
72 throw new RestException(404, 'MO not found');
73 }
74
75 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
76 throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
77 }
78
79 return $this->_cleanObjectDatas($this->mo);
80 }
81
82
98 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
99 {
100 global $db, $conf;
101
102 if (!DolibarrApiAccess::$user->rights->mrp->read) {
103 throw new RestException(401);
104 }
105
106 $obj_ret = array();
107 $tmpobject = new Mo($this->db);
108
109 $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
110
111 $restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
112
113 // If the internal user must only see his customers, force searching by him
114 $search_sale = 0;
115 if ($restrictonsocid && !DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) {
116 $search_sale = DolibarrApiAccess::$user->id;
117 }
118
119 $sql = "SELECT t.rowid";
120 if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
121 $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
122 }
123 $sql .= " FROM ".MAIN_DB_PREFIX.$tmpobject->table_element." AS t 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
125 if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
126 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
127 }
128 $sql .= " WHERE 1 = 1";
129
130 // Example of use $mode
131 //if ($mode == 1) $sql.= " AND s.client IN (1, 3)";
132 //if ($mode == 2) $sql.= " AND s.client IN (2, 3)";
133
134 if ($tmpobject->ismultientitymanaged) {
135 $sql .= ' AND t.entity IN ('.getEntity($tmpobject->element).')';
136 }
137 if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
138 $sql .= " AND t.fk_soc = sc.fk_soc";
139 }
140 if ($restrictonsocid && $socid) {
141 $sql .= " AND t.fk_soc = ".((int) $socid);
142 }
143 if ($restrictonsocid && $search_sale > 0) {
144 $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
145 }
146 // Insert sale filter
147 if ($restrictonsocid && $search_sale > 0) {
148 $sql .= " AND sc.fk_user = ".((int) $search_sale);
149 }
150 if ($sqlfilters) {
151 $errormessage = '';
152 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
153 if ($errormessage) {
154 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
155 }
156 }
157
158 $sql .= $this->db->order($sortfield, $sortorder);
159 if ($limit) {
160 if ($page < 0) {
161 $page = 0;
162 }
163 $offset = $limit * $page;
164
165 $sql .= $this->db->plimit($limit + 1, $offset);
166 }
167
168 $result = $this->db->query($sql);
169 if ($result) {
170 $num = $this->db->num_rows($result);
171 $i = 0;
172 while ($i < $num) {
173 $obj = $this->db->fetch_object($result);
174 $tmp_object = new Mo($this->db);
175 if ($tmp_object->fetch($obj->rowid)) {
176 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($tmp_object), $properties);
177 }
178 $i++;
179 }
180 } else {
181 throw new RestException(503, 'Error when retrieve MO list');
182 }
183
184 return $obj_ret;
185 }
186
193 public function post($request_data = null)
194 {
195 if (!DolibarrApiAccess::$user->rights->mrp->write) {
196 throw new RestException(401);
197 }
198 // Check mandatory fields
199 $result = $this->_validate($request_data);
200
201 foreach ($request_data as $field => $value) {
202 if ($field === 'caller') {
203 // 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 whith the caller
204 $this->mo->context['caller'] = $request_data['caller'];
205 continue;
206 }
207
208 $this->mo->$field = $value;
209 }
210
211 $this->checkRefNumbering();
212
213 if (!$this->mo->create(DolibarrApiAccess::$user)) {
214 throw new RestException(500, "Error creating MO", array_merge(array($this->mo->error), $this->mo->errors));
215 }
216 return $this->mo->id;
217 }
218
227 public function put($id, $request_data = null)
228 {
229 if (!DolibarrApiAccess::$user->rights->mrp->write) {
230 throw new RestException(401);
231 }
232
233 $result = $this->mo->fetch($id);
234 if (!$result) {
235 throw new RestException(404, 'MO not found');
236 }
237
238 if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
239 throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
240 }
241
242 foreach ($request_data as $field => $value) {
243 if ($field == 'id') {
244 continue;
245 }
246 if ($field === 'caller') {
247 // 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 whith the caller
248 $this->mo->context['caller'] = $request_data['caller'];
249 continue;
250 }
251
252 if ($field == 'array_options' && is_array($value)) {
253 foreach ($value as $index => $val) {
254 $this->mo->array_options[$index] = $this->_checkValForAPI($field, $val, $this->mo);
255 }
256 continue;
257 }
258
259 $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
260 }
261
262 $this->checkRefNumbering();
263
264 if ($this->mo->update(DolibarrApiAccess::$user) > 0) {
265 return $this->get($id);
266 } else {
267 throw new RestException(500, $this->mo->error);
268 }
269 }
270
277 public function delete($id)
278 {
279 if (!DolibarrApiAccess::$user->rights->mrp->delete) {
280 throw new RestException(401);
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(401, '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
334 public function produceAndConsumeAll($id, $request_data = null)
335 {
336 global $langs;
337
338 $error = 0;
339
340 if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
341 throw new RestException(403, 'Not enough permission');
342 }
343 $result = $this->mo->fetch($id);
344 if (!$result) {
345 throw new RestException(404, 'MO not found');
346 }
347
348 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
349 throw new RestException(405, 'Error bad status of MO');
350 }
351
352 // Code for consume and produce...
353 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
354 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
355 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
356
357 $stockmove = new MouvementStock($this->db);
358
359 $labelmovement = '';
360 $codemovement = '';
361 $autoclose = 1;
362 $arraytoconsume = array();
363 $arraytoproduce = array();
364
365 foreach ($request_data as $field => $value) {
366 if ($field == 'inventorylabel') {
367 $labelmovement = $value;
368 }
369 if ($field == 'inventorycode') {
370 $codemovement = $value;
371 }
372 if ($field == 'autoclose') {
373 $autoclose = $value;
374 }
375 if ($field == 'arraytoconsume') {
376 $arraytoconsume = $value;
377 }
378 if ($field == 'arraytoproduce') {
379 $arraytoproduce = $value;
380 }
381 if ($field === 'caller') {
382 // 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 whith the caller
383 $stockmove->context['caller'] = $request_data['caller'];
384 continue;
385 }
386 }
387
388 if (empty($labelmovement)) {
389 throw new RestException(500, "Field inventorylabel not provided");
390 }
391 if (empty($codemovement)) {
392 throw new RestException(500, "Field inventorycode not provided");
393 }
394
395 $consumptioncomplete = true;
396 $productioncomplete = true;
397
398 if (!empty($arraytoconsume) && !empty($arraytoproduce)) {
399 $pos = 0;
400 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
401 foreach ($arrayofarrayname as $arrayname) {
402 foreach (${$arrayname} as $value) {
403 $tmpproduct = new Product($this->db);
404 if (empty($value["objectid"])) {
405 throw new RestException(500, "Field objectid required in ".$arrayname);
406 }
407 $tmpproduct->fetch($value["qty"]);
408 if (empty($value["qty"])) {
409 throw new RestException(500, "Field qty required in ".$arrayname);
410 }
411 if ($value["qty"] != 0) {
412 $qtytoprocess = $value["qty"];
413 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
414 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
415 $error++;
416 throw new RestException(500, "Field fk_warehouse must be > 0 in ".$arrayname);
417 }
418 if ($tmpproduct->status_batch) {
419 $error++;
420 throw new RestException(500, "Product ".$tmpproduct->ref."must be in batch");
421 }
422 }
423 $idstockmove = 0;
424 if (!$error && $value["fk_warehouse"] > 0) {
425 // Record consumption to do and stock movement
426 $id_product_batch = 0;
427
428 $stockmove->setOrigin($this->mo->element, $this->mo->id);
429
430 if ($arrayname == 'arraytoconsume') {
431 $moline = new MoLine($this->db);
432 $moline->fk_mo = $this->mo->id;
433 $moline->position = $pos;
434 $moline->fk_product = $value["objectid"];
435 $moline->fk_warehouse = $value["fk_warehouse"];
436 $moline->qty = $qtytoprocess;
437 $moline->batch = $tmpproduct->status_batch;
438 $moline->role = 'toproduce';
439 $moline->fk_mrp_production = "";
440 $moline->fk_stock_movement = $idstockmove;
441 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
442
443 $resultmoline = $moline->create(DolibarrApiAccess::$user);
444 if ($resultmoline <= 0) {
445 $error++;
446 throw new RestException(500, $moline->error);
447 }
448 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
449 } else {
450 $moline = new MoLine($this->db);
451 $moline->fk_mo = $this->mo->id;
452 $moline->position = $pos;
453 $moline->fk_product = $value["objectid"];
454 $moline->fk_warehouse = $value["fk_warehouse"];
455 $moline->qty = $qtytoprocess;
456 $moline->batch = $tmpproduct->status_batch;
457 $moline->role = 'toconsume';
458 $moline->fk_mrp_production = "";
459 $moline->fk_stock_movement = $idstockmove;
460 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
461
462 $resultmoline = $moline->create(DolibarrApiAccess::$user);
463 if ($resultmoline <= 0) {
464 $error++;
465 throw new RestException(500, $moline->error);
466 }
467 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
468 }
469 if ($idstockmove < 0) {
470 $error++;
471 throw new RestException(500, $stockmove->error);
472 }
473 }
474 if (!$error) {
475 // Record consumption done
476 $moline = new MoLine($this->db);
477 $moline->fk_mo = $this->mo->id;
478 $moline->position = $pos;
479 $moline->fk_product = $value["objectid"];
480 $moline->fk_warehouse = $value["fk_warehouse"];
481 $moline->qty = $qtytoprocess;
482 $moline->batch = $tmpproduct->status_batch;
483 if ($arrayname == "arraytoconsume") {
484 $moline->role = 'consumed';
485 } else {
486 $moline->role = 'produced';
487 }
488 $moline->fk_mrp_production = "";
489 $moline->fk_stock_movement = $idstockmove;
490 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
491
492 $resultmoline = $moline->create(DolibarrApiAccess::$user);
493 if ($resultmoline <= 0) {
494 $error++;
495 throw new RestException(500, $moline->error);
496 }
497
498 $pos++;
499 }
500 }
501 }
502 }
503 if (!$error) {
504 if ($autoclose <= 0) {
505 $consumptioncomplete = false;
506 $productioncomplete = false;
507 }
508 }
509 } else {
510 $pos = 0;
511 foreach ($this->mo->lines as $line) {
512 if ($line->role == 'toconsume') {
513 $tmpproduct = new Product($this->db);
514 $tmpproduct->fetch($line->fk_product);
515 if ($line->qty != 0) {
516 $qtytoprocess = $line->qty;
517 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
518 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
519 $langs->load("errors");
520 $error++;
521 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
522 }
523 if ($tmpproduct->status_batch) {
524 $langs->load("errors");
525 $error++;
526 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
527 }
528 }
529 $idstockmove = 0;
530 if (!$error && $line->fk_warehouse > 0) {
531 // Record stock movement
532 $id_product_batch = 0;
533 $stockmove->origin_type = 'mo';
534 $stockmove->origin_id = $this->mo->id;
535 if ($qtytoprocess >= 0) {
536 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
537 } else {
538 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
539 }
540 if ($idstockmove < 0) {
541 $error++;
542 throw new RestException(500, $stockmove->error);
543 }
544 }
545 if (!$error) {
546 // Record consumption
547 $moline = new MoLine($this->db);
548 $moline->fk_mo = $this->mo->id;
549 $moline->position = $pos;
550 $moline->fk_product = $line->fk_product;
551 $moline->fk_warehouse = $line->fk_warehouse;
552 $moline->qty = $qtytoprocess;
553 $moline->batch = $tmpproduct->status_batch;
554 $moline->role = 'consumed';
555 $moline->fk_mrp_production = $line->id;
556 $moline->fk_stock_movement = $idstockmove;
557 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
558
559 $resultmoline = $moline->create(DolibarrApiAccess::$user);
560 if ($resultmoline <= 0) {
561 $error++;
562 throw new RestException(500, $moline->error);
563 }
564
565 $pos++;
566 }
567 }
568 }
569 }
570 $pos = 0;
571 foreach ($this->mo->lines as $line) {
572 if ($line->role == 'toproduce') {
573 $tmpproduct = new Product($this->db);
574 $tmpproduct->fetch($line->fk_product);
575 if ($line->qty != 0) {
576 $qtytoprocess = $line->qty;
577 if (isset($line->fk_warehouse)) { // If there is a warehouse to set
578 if (!($line->fk_warehouse > 0)) { // If there is no warehouse set.
579 $langs->load("errors");
580 $error++;
581 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
582 }
583 if ($tmpproduct->status_batch) {
584 $langs->load("errors");
585 $error++;
586 throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
587 }
588 }
589 $idstockmove = 0;
590 if (!$error && $line->fk_warehouse > 0) {
591 // Record stock movement
592 $id_product_batch = 0;
593 $stockmove->origin_type = 'mo';
594 $stockmove->origin_id = $this->mo->id;
595 if ($qtytoprocess >= 0) {
596 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
597 } else {
598 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
599 }
600 if ($idstockmove < 0) {
601 $error++;
602 throw new RestException(500, $stockmove->error);
603 }
604 }
605 if (!$error) {
606 // Record consumption
607 $moline = new MoLine($this->db);
608 $moline->fk_mo = $this->mo->id;
609 $moline->position = $pos;
610 $moline->fk_product = $line->fk_product;
611 $moline->fk_warehouse = $line->fk_warehouse;
612 $moline->qty = $qtytoprocess;
613 $moline->batch = $tmpproduct->status_batch;
614 $moline->role = 'produced';
615 $moline->fk_mrp_production = $line->id;
616 $moline->fk_stock_movement = $idstockmove;
617 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
618
619 $resultmoline = $moline->create(DolibarrApiAccess::$user);
620 if ($resultmoline <= 0) {
621 $error++;
622 throw new RestException(500, $moline->error);
623 }
624
625 $pos++;
626 }
627 }
628 }
629 }
630
631 if (!$error) {
632 if ($autoclose > 0) {
633 foreach ($this->mo->lines as $line) {
634 if ($line->role == 'toconsume') {
635 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
636 $alreadyconsumed = 0;
637 foreach ($arrayoflines as $line2) {
638 $alreadyconsumed += $line2['qty'];
639 }
640
641 if ($alreadyconsumed < $line->qty) {
642 $consumptioncomplete = false;
643 }
644 }
645 if ($line->role == 'toproduce') {
646 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
647 $alreadyproduced = 0;
648 foreach ($arrayoflines as $line2) {
649 $alreadyproduced += $line2['qty'];
650 }
651
652 if ($alreadyproduced < $line->qty) {
653 $productioncomplete = false;
654 }
655 }
656 }
657 } else {
658 $consumptioncomplete = false;
659 $productioncomplete = false;
660 }
661 }
662 }
663
664 // Update status of MO
665 dol_syslog("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
666 //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$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
711 public function produceAndConsume($id, $request_data = null)
712 {
713 if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
714 throw new RestException(403, 'Not enough permission');
715 }
716 $result = $this->mo->fetch($id);
717 if (!$result) {
718 throw new RestException(404, 'MO not found');
719 }
720
721 if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
722 throw new RestException(405, 'Error bad status of MO');
723 }
724
725 // Code for consume and produce...
726 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
727 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
728 require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
729
730 $stockmove = new MouvementStock($this->db);
731
732 $labelmovement = '';
733 $codemovement = '';
734 $autoclose = 1;
735 $arraytoconsume = array();
736 $arraytoproduce = array();
737
738 foreach ($request_data as $field => $value) {
739 if ($field == 'inventorylabel') {
740 $labelmovement = $value;
741 }
742 if ($field == 'inventorycode') {
743 $codemovement = $value;
744 }
745 if ($field == 'autoclose') {
746 $autoclose = $value;
747 }
748 if ($field == 'arraytoconsume') {
749 $arraytoconsume = $value;
750 }
751 if ($field == 'arraytoproduce') {
752 $arraytoproduce = $value;
753 }
754 if ($field === 'caller') {
755 // 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 whith the caller
756 $stockmove->context['caller'] = $request_data['caller'];
757 continue;
758 }
759 }
760
761 if (empty($labelmovement)) {
762 throw new RestException(500, "Field inventorylabel not provided");
763 }
764 if (empty($codemovement)) {
765 throw new RestException(500, "Field inventorycode not provided");
766 }
767
768 $this->db->begin();
769
770 $pos = 0;
771 $arrayofarrayname = array("arraytoconsume","arraytoproduce");
772 foreach ($arrayofarrayname as $arrayname) {
773 foreach (${$arrayname} as $value) {
774 if (empty($value["objectid"])) {
775 throw new RestException(500, "Field objectid required in " . $arrayname);
776 }
777
778 $molinetoprocess = new MoLine($this->db);
779 $tmpmolineid = $molinetoprocess->fetch($value["objectid"]);
780 if ($tmpmolineid <= 0) {
781 throw new RestException(500, "MoLine with rowid " . $value["objectid"] . " not exist.");
782 }
783
784 $tmpproduct = new Product($this->db);
785 $tmpproduct->fetch($molinetoprocess->fk_product);
786 if ($tmpproduct->status_batch) {
787 throw new RestException(500, "Product " . $tmpproduct->ref . " must be in batch, this API can't handle it currently.");
788 }
789
790 if (empty($value["qty"]) && $value["qty"] != 0) {
791 throw new RestException(500, "Field qty with lower or higher then 0 required in " . $arrayname);
792 }
793 $qtytoprocess = $value["qty"];
794
795 $fk_warehousetoprocess = 0;
796 if ($molinetoprocess->disable_stock_change == false) {
797 if (isset($value["fk_warehouse"])) { // If there is a warehouse to set
798 if (!($value["fk_warehouse"] > 0)) { // If there is no warehouse set.
799 throw new RestException(500, "Field fk_warehouse required in " . $arrayname);
800 }
801 }
802 $fk_warehousetoprocess = (int) $value["fk_warehouse"];
803 }
804
805 $pricetoproduce = 0;
806 if (isset($value["pricetoproduce"])) { // If there is a price to produce set.
807 if ($value["pricetoproduce"] > 0) { // Only use prices grater then 0.
808 $pricetoproduce = $value["pricetoproduce"];
809 }
810 }
811
812 $pos = 0;
813 $idstockmove = 0;
814
815 if ($molinetoprocess->disable_stock_change == false) {
816 // Record stock movement
817 $id_product_batch = 0;
818 $stockmove->origin_type = 'mo';
819 $stockmove->origin_id = $this->mo->id;
820 if ($arrayname == "arraytoconsume") {
821 if ($qtytoprocess >= 0) {
822 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
823 } else {
824 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
825 }
826 } else {
827 if ($qtytoprocess >= 0) {
828 $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, $pricetoproduce, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
829 } else {
830 $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
831 }
832 }
833 if ($idstockmove <= 0) {
834 throw new RestException(500, $stockmove->error);
835 }
836 }
837
838 // Record consumption
839 $moline = new MoLine($this->db);
840 $moline->fk_mo = $this->mo->id;
841 $moline->position = $pos;
842 $moline->fk_product = $tmpproduct->id;
843 $moline->fk_warehouse = $idstockmove > 0 ? $fk_warehousetoprocess : null;
844 $moline->qty = $qtytoprocess;
845 $moline->batch = '';
846 $moline->fk_mrp_production = $molinetoprocess->id;
847 $moline->fk_stock_movement = $idstockmove > 0 ? $idstockmove : null;
848 $moline->fk_user_creat = DolibarrApiAccess::$user->id;
849
850 if ($arrayname == "arraytoconsume") {
851 $moline->role = 'consumed';
852 } else {
853 $moline->role = 'produced';
854 }
855
856 $resultmoline = $moline->create(DolibarrApiAccess::$user);
857 if ($resultmoline <= 0) {
858 throw new RestException(500, $moline->error);
859 }
860 }
861 }
862
863 $consumptioncomplete = true;
864 $productioncomplete = true;
865
866 if ($autoclose > 0) {
867 // Refresh Lines after consumptions.
868 $this->mo->fetchLines();
869
870 foreach ($this->mo->lines as $line) {
871 if ($line->role == 'toconsume') {
872 $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
873 $alreadyconsumed = 0;
874 foreach ($arrayoflines as $line2) {
875 $alreadyconsumed += $line2['qty'];
876 }
877
878 if ($alreadyconsumed < $line->qty) {
879 $consumptioncomplete = false;
880 }
881 }
882 if ($line->role == 'toproduce') {
883 $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
884 $alreadyproduced = 0;
885 foreach ($arrayoflines as $line2) {
886 $alreadyproduced += $line2['qty'];
887 }
888
889 if ($alreadyproduced < $line->qty) {
890 $productioncomplete = false;
891 }
892 }
893 }
894 } else {
895 $consumptioncomplete = false;
896 $productioncomplete = false;
897 }
898
899 // Update status of MO
900 dol_syslog("consumptioncomplete = " . $consumptioncomplete . " productioncomplete = " . $productioncomplete);
901 //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
902 if ($consumptioncomplete && $productioncomplete) {
903 $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
904 } else {
905 $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
906 }
907 if ($result <= 0) {
908 throw new RestException(500, $this->mo->error);
909 }
910
911 $this->db->commit();
912 return $this->mo->id;
913 }
914
915
916 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
923 protected function _cleanObjectDatas($object)
924 {
925 // phpcs:enable
926 $object = parent::_cleanObjectDatas($object);
927
928 unset($object->rowid);
929 unset($object->canvas);
930
931 unset($object->name);
932 unset($object->lastname);
933 unset($object->firstname);
934 unset($object->civility_id);
935 unset($object->statut);
936 unset($object->state);
937 unset($object->state_id);
938 unset($object->state_code);
939 unset($object->region);
940 unset($object->region_code);
941 unset($object->country);
942 unset($object->country_id);
943 unset($object->country_code);
944 unset($object->barcode_type);
945 unset($object->barcode_type_code);
946 unset($object->barcode_type_label);
947 unset($object->barcode_type_coder);
948 unset($object->total_ht);
949 unset($object->total_tva);
950 unset($object->total_localtax1);
951 unset($object->total_localtax2);
952 unset($object->total_ttc);
953 unset($object->fk_account);
954 unset($object->comments);
955 unset($object->note);
956 unset($object->mode_reglement_id);
957 unset($object->cond_reglement_id);
958 unset($object->cond_reglement);
959 unset($object->shipping_method_id);
960 unset($object->fk_incoterms);
961 unset($object->label_incoterms);
962 unset($object->location_incoterms);
963
964 // If object has lines, remove $db property
965 if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
966 $nboflines = count($object->lines);
967 for ($i = 0; $i < $nboflines; $i++) {
968 $this->_cleanObjectDatas($object->lines[$i]);
969
970 unset($object->lines[$i]->lines);
971 unset($object->lines[$i]->note);
972 }
973 }
974
975 return $object;
976 }
977
986 private function _validate($data)
987 {
988 $myobject = array();
989 foreach ($this->mo->fields as $field => $propfield) {
990 if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || $propfield['notnull'] != 1) {
991 continue; // Not a mandatory field
992 }
993 if (!isset($data[$field])) {
994 throw new RestException(400, "$field field missing");
995 }
996 $myobject[$field] = $data[$field];
997 }
998 return $myobject;
999 }
1000
1006 private function checkRefNumbering(): void
1007 {
1008 $ref = substr($this->mo->ref, 1, 4);
1009 if ($this->mo->status > 0 && $ref == 'PROV') {
1010 throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
1011 }
1012
1013 if (strtolower($this->mo->ref) == 'auto') {
1014 if (empty($this->mo->id) && $this->mo->status == 0) {
1015 $this->mo->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
1016 } else {
1017 $this->mo->fetch_product();
1018 $numref = $this->mo->getNextNumRef($this->mo->product);
1019 $this->mo->ref = $numref;
1020 }
1021 }
1022 }
1023}
Class for API REST v1.
Definition api.class.php:31
_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:85
Class for Mo.
Definition mo.class.php:34
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 create or update 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.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.