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