Passed
Push — EXTRACT_CLASSES ( 9f3ede...ff35ec )
by Rafael
76:09 queued 20:57
created

Mos::index()   F

Complexity

Conditions 20
Paths 1601

Size

Total Lines 72
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 46
nc 1601
nop 6
dl 0
loc 72
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2015       Jean-François Ferry         <[email protected]>
4
 * Copyright (C) 2019       Maxime Kohlhaas             <[email protected]>
5
 * Copyright (C) 2024		MDW							<[email protected]>
6
 * Copyright (C) 2024       Rafael San José             <[email protected]>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Dolibarr\Code\Mrp\Api;
23
24
use Dolibarr\Core\Base\DolibarrApi;
25
use Luracast\Restler\RestException;
26
27
require_once constant('DOL_DOCUMENT_ROOT') . '/mrp/class/mo.class.php';
28
29
/**
30
 * \file    htdocs/mrp/class/api_mos.class.php
31
 * \ingroup mrp
32
 * \brief   File for API management of MO.
33
 */
34
35
/**
36
 * API class for MO
37
 *
38
 * @access protected
39
 * @class  DolibarrApiAccess {@requires user,external}
40
 */
41
class Mos extends DolibarrApi
42
{
43
    /**
44
     * @var Mo $mo {@type Mo}
45
     */
46
    public $mo;
47
48
    /**
49
     * Constructor
50
     */
51
    public function __construct()
52
    {
53
        global $db, $conf;
54
        $this->db = $db;
55
        $this->mo = new Mo($this->db);
56
    }
57
58
    /**
59
     * Get properties of a MO object
60
     *
61
     * Return an array with MO information
62
     *
63
     * @param   int     $id             ID of MO
64
     * @return  Object                  Object with cleaned properties
65
     *
66
     * @url GET {id}
67
     * @throws  RestException
68
     */
69
    public function get($id)
70
    {
71
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
72
            throw new RestException(403);
73
        }
74
75
        $result = $this->mo->fetch($id);
76
        if (!$result) {
77
            throw new RestException(404, 'MO not found');
78
        }
79
80
        if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
81
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
82
        }
83
84
        return $this->_cleanObjectDatas($this->mo);
85
    }
86
87
88
    /**
89
     * List Mos
90
     *
91
     * Get a list of MOs
92
     *
93
     * @param string           $sortfield           Sort field
94
     * @param string           $sortorder           Sort order
95
     * @param int              $limit               Limit for list
96
     * @param int              $page                Page number
97
     * @param string           $sqlfilters          Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
98
     * @param string           $properties          Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
99
     * @return  array                               Array of order objects
100
     *
101
     * @throws RestException
102
     */
103
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
104
    {
105
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'read')) {
106
            throw new RestException(403);
107
        }
108
109
        $obj_ret = array();
110
        $tmpobject = new Mo($this->db);
111
112
        $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : 0;
113
114
        $restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
115
116
        // If the internal user must only see his customers, force searching by him
117
        $search_sale = 0;
118
        if ($restrictonsocid && !DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
119
            $search_sale = DolibarrApiAccess::$user->id;
120
        }
121
122
        $sql = "SELECT t.rowid";
123
        $sql .= " FROM " . MAIN_DB_PREFIX . $tmpobject->table_element . " AS t";
124
        $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
125
        $sql .= " WHERE 1 = 1";
126
        if ($tmpobject->ismultientitymanaged) {
127
            $sql .= ' AND t.entity IN (' . getEntity($tmpobject->element) . ')';
128
        }
129
        if ($restrictonsocid && $socid) {
130
            $sql .= " AND t.fk_soc = " . ((int) $socid);
131
        }
132
        // Search on sale representative
133
        if ($search_sale && $search_sale != '-1') {
134
            if ($search_sale == -2) {
135
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
136
            } elseif ($search_sale > 0) {
137
                $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) . ")";
138
            }
139
        }
140
        if ($sqlfilters) {
141
            $errormessage = '';
142
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
143
            if ($errormessage) {
144
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
145
            }
146
        }
147
148
        $sql .= $this->db->order($sortfield, $sortorder);
149
        if ($limit) {
150
            if ($page < 0) {
151
                $page = 0;
152
            }
153
            $offset = $limit * $page;
154
155
            $sql .= $this->db->plimit($limit + 1, $offset);
156
        }
157
158
        $result = $this->db->query($sql);
159
        if ($result) {
160
            $num = $this->db->num_rows($result);
161
            $i = 0;
162
            while ($i < $num) {
163
                $obj = $this->db->fetch_object($result);
164
                $tmp_object = new Mo($this->db);
165
                if ($tmp_object->fetch($obj->rowid)) {
166
                    $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($tmp_object), $properties);
167
                }
168
                $i++;
169
            }
170
        } else {
171
            throw new RestException(503, 'Error when retrieve MO list');
172
        }
173
174
        return $obj_ret;
175
    }
176
177
    /**
178
     * Create MO object
179
     *
180
     * @param array $request_data   Request datas
181
     * @return int  ID of MO
182
     */
183
    public function post($request_data = null)
184
    {
185
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
186
            throw new RestException(403);
187
        }
188
        // Check mandatory fields
189
        $result = $this->_validate($request_data);
190
191
        foreach ($request_data as $field => $value) {
192
            if ($field === 'caller') {
193
                // 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
194
                $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
195
                continue;
196
            }
197
198
            $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
199
        }
200
201
        $this->checkRefNumbering();
202
203
        if (!$this->mo->create(DolibarrApiAccess::$user)) {
204
            throw new RestException(500, "Error creating MO", array_merge(array($this->mo->error), $this->mo->errors));
205
        }
206
        return $this->mo->id;
207
    }
208
209
    /**
210
     * Update MO
211
     *
212
     * @param   int     $id                 Id of MO to update
213
     * @param   array   $request_data       Datas
214
     * @return  Object                      Updated object
215
     */
216
    public function put($id, $request_data = null)
217
    {
218
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
219
            throw new RestException(403);
220
        }
221
222
        $result = $this->mo->fetch($id);
223
        if (!$result) {
224
            throw new RestException(404, 'MO not found');
225
        }
226
227
        if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
228
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
229
        }
230
231
        foreach ($request_data as $field => $value) {
232
            if ($field == 'id') {
233
                continue;
234
            }
235
            if ($field === 'caller') {
236
                // 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
237
                $this->mo->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
238
                continue;
239
            }
240
241
            $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo);
242
        }
243
244
        $this->checkRefNumbering();
245
246
        if ($this->mo->update(DolibarrApiAccess::$user) > 0) {
247
            return $this->get($id);
248
        } else {
249
            throw new RestException(500, $this->mo->error);
250
        }
251
    }
252
253
    /**
254
     * Delete MO
255
     *
256
     * @param   int     $id   MO ID
257
     * @return  array
258
     */
259
    public function delete($id)
260
    {
261
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'delete')) {
262
            throw new RestException(403);
263
        }
264
        $result = $this->mo->fetch($id);
265
        if (!$result) {
266
            throw new RestException(404, 'MO not found');
267
        }
268
269
        if (!DolibarrApi::_checkAccessToResource('mrp', $this->mo->id, 'mrp_mo')) {
270
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
271
        }
272
273
        if (!$this->mo->delete(DolibarrApiAccess::$user)) {
274
            throw new RestException(500, 'Error when deleting MO : ' . $this->mo->error);
275
        }
276
277
        return array(
278
            'success' => array(
279
                'code' => 200,
280
                'message' => 'MO deleted'
281
            )
282
        );
283
    }
284
285
286
    /**
287
     * Produce and consume all
288
     *
289
     * - If arraytoconsume and arraytoproduce are both filled, this fill an empty MO with the lines to consume and produce and record the consumption and production.
290
     * - If arraytoconsume and arraytoproduce are not provided, it consumes and produces all existing lines.
291
     *
292
     * Example:
293
     * {
294
     *   "inventorylabel": "Produce and consume using API",
295
     *   "inventorycode": "PRODUCEAPI-YY-MM-DD",
296
     *   "autoclose": 1,
297
     *   "arraytoconsume": [
298
     *       "objectid": 123, -- ID_of_product
299
     *       "qty": "2",
300
     *       "fk_warehouse": "789"
301
     *   ],
302
     *   "arraytoproduce": [
303
     *       "objectid": 456, -- ID_of_product
304
     *       "qty": "1",
305
     *       "fk_warehouse": "789"
306
     *   ]
307
     * }
308
     *
309
     * @param int       $id             ID of state
310
     * @param array     $request_data   Request datas
311
     *
312
     * @url     POST {id}/produceandconsumeall
313
     *
314
     * @return int  ID of MO
315
     */
316
    public function produceAndConsumeAll($id, $request_data = null)
317
    {
318
        global $langs;
319
320
        $error = 0;
321
322
        if (!DolibarrApiAccess::$user->hasRight('mrp', 'write')) {
323
            throw new RestException(403, 'Not enough permission');
324
        }
325
        $result = $this->mo->fetch($id);
326
        if (!$result) {
327
            throw new RestException(404, 'MO not found');
328
        }
329
330
        if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
331
            throw new RestException(405, 'Error bad status of MO');
332
        }
333
334
        // Code for consume and produce...
335
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
336
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
337
        require_once constant('DOL_DOCUMENT_ROOT') . '/mrp/lib/mrp_mo.lib.php';
338
339
        $stockmove = new MouvementStock($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Mrp\Api\MouvementStock was not found. Did you mean MouvementStock? If so, make sure to prefix the type with \.
Loading history...
340
341
        $labelmovement = '';
342
        $codemovement = '';
343
        $autoclose = 1;
344
        $arraytoconsume = array();
345
        $arraytoproduce = array();
346
347
        foreach ($request_data as $field => $value) {
348
            if ($field == 'inventorylabel') {
349
                $labelmovement = $value;
350
            }
351
            if ($field == 'inventorycode') {
352
                $codemovement = $value;
353
            }
354
            if ($field == 'autoclose') {
355
                $autoclose = $value;
356
            }
357
            if ($field == 'arraytoconsume') {
358
                $arraytoconsume = $value;
359
            }
360
            if ($field == 'arraytoproduce') {
361
                $arraytoproduce = $value;
362
            }
363
            if ($field === 'caller') {
364
                // 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
365
                $stockmove->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
366
                continue;
367
            }
368
        }
369
370
        if (empty($labelmovement)) {
371
            throw new RestException(500, "Field inventorylabel not provided");
372
        }
373
        if (empty($codemovement)) {
374
            throw new RestException(500, "Field inventorycode not provided");
375
        }
376
377
        $consumptioncomplete = true;
378
        $productioncomplete = true;
379
380
        if (!empty($arraytoconsume) && !empty($arraytoproduce)) {
381
            $pos = 0;
382
            $arrayofarrayname = array("arraytoconsume","arraytoproduce");
383
            foreach ($arrayofarrayname as $arrayname) {
384
                foreach (${$arrayname} as $value) {
385
                    $tmpproduct = new Product($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Mrp\Api\Product was not found. Did you mean Product? If so, make sure to prefix the type with \.
Loading history...
386
                    if (empty($value["objectid"])) {
387
                        throw new RestException(500, "Field objectid required in " . $arrayname);
388
                    }
389
                    $tmpproduct->fetch($value["qty"]);
390
                    if (empty($value["qty"])) {
391
                        throw new RestException(500, "Field qty required in " . $arrayname);
392
                    }
393
                    if ($value["qty"] != 0) {
394
                        $qtytoprocess = $value["qty"];
395
                        if (isset($value["fk_warehouse"])) {    // If there is a warehouse to set
396
                            if (!($value["fk_warehouse"] > 0)) {    // If there is no warehouse set.
397
                                $error++;
398
                                throw new RestException(500, "Field fk_warehouse must be > 0 in " . $arrayname);
399
                            }
400
                            if ($tmpproduct->status_batch) {
401
                                $error++;
402
                                throw new RestException(500, "Product " . $tmpproduct->ref . "must be in batch");
403
                            }
404
                        }
405
                        $idstockmove = 0;
406
                        if (!$error && $value["fk_warehouse"] > 0) {
407
                            // Record consumption to do and stock movement
408
                            $id_product_batch = 0;
409
410
                            $stockmove->setOrigin($this->mo->element, $this->mo->id);
411
412
                            if ($arrayname == 'arraytoconsume') {
413
                                $moline = new MoLine($this->db);
414
                                $moline->fk_mo = $this->mo->id;
415
                                $moline->position = $pos;
416
                                $moline->fk_product = $value["objectid"];
417
                                $moline->fk_warehouse = $value["fk_warehouse"];
418
                                $moline->qty = $qtytoprocess;
419
                                $moline->batch = $tmpproduct->status_batch;
420
                                $moline->role = 'toproduce';
421
                                $moline->fk_mrp_production = "";
422
                                $moline->fk_stock_movement = $idstockmove;
423
                                $moline->fk_user_creat = DolibarrApiAccess::$user->id;
424
425
                                $resultmoline = $moline->create(DolibarrApiAccess::$user);
426
                                if ($resultmoline <= 0) {
427
                                    $error++;
428
                                    throw new RestException(500, $moline->error);
429
                                }
430
                                $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
431
                            } else {
432
                                $moline = new MoLine($this->db);
433
                                $moline->fk_mo = $this->mo->id;
434
                                $moline->position = $pos;
435
                                $moline->fk_product = $value["objectid"];
436
                                $moline->fk_warehouse = $value["fk_warehouse"];
437
                                $moline->qty = $qtytoprocess;
438
                                $moline->batch = $tmpproduct->status_batch;
439
                                $moline->role = 'toconsume';
440
                                $moline->fk_mrp_production = "";
441
                                $moline->fk_stock_movement = $idstockmove;
442
                                $moline->fk_user_creat = DolibarrApiAccess::$user->id;
443
444
                                $resultmoline = $moline->create(DolibarrApiAccess::$user);
445
                                if ($resultmoline <= 0) {
446
                                    $error++;
447
                                    throw new RestException(500, $moline->error);
448
                                }
449
                                $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $value["objectid"], $value["fk_warehouse"], $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
450
                            }
451
                            if ($idstockmove < 0) {
452
                                $error++;
453
                                throw new RestException(500, $stockmove->error);
454
                            }
455
                        }
456
                        if (!$error) {
457
                            // Record consumption done
458
                            $moline = new MoLine($this->db);
459
                            $moline->fk_mo = $this->mo->id;
460
                            $moline->position = $pos;
461
                            $moline->fk_product = $value["objectid"];
462
                            $moline->fk_warehouse = $value["fk_warehouse"];
463
                            $moline->qty = $qtytoprocess;
464
                            $moline->batch = $tmpproduct->status_batch;
465
                            if ($arrayname == "arraytoconsume") {
466
                                $moline->role = 'consumed';
467
                            } else {
468
                                $moline->role = 'produced';
469
                            }
470
                            $moline->fk_mrp_production = "";
471
                            $moline->fk_stock_movement = $idstockmove;
472
                            $moline->fk_user_creat = DolibarrApiAccess::$user->id;
473
474
                            $resultmoline = $moline->create(DolibarrApiAccess::$user);
475
                            if ($resultmoline <= 0) {
476
                                $error++;
477
                                throw new RestException(500, $moline->error);
478
                            }
479
480
                            $pos++;
481
                        }
482
                    }
483
                }
484
            }
485
            if (!$error) {
486
                if ($autoclose <= 0) {
487
                    $consumptioncomplete = false;
488
                    $productioncomplete = false;
489
                }
490
            }
491
        } else {
492
            $pos = 0;
493
            foreach ($this->mo->lines as $line) {
494
                if ($line->role == 'toconsume') {
495
                    $tmpproduct = new Product($this->db);
496
                    $tmpproduct->fetch($line->fk_product);
497
                    if ($line->qty != 0) {
498
                        $qtytoprocess = $line->qty;
499
                        if (isset($line->fk_warehouse)) {   // If there is a warehouse to set
500
                            if (!($line->fk_warehouse > 0)) {   // If there is no warehouse set.
501
                                $langs->load("errors");
502
                                $error++;
503
                                throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
504
                            }
505
                            if ($tmpproduct->status_batch) {
506
                                $langs->load("errors");
507
                                $error++;
508
                                throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
509
                            }
510
                        }
511
                        $idstockmove = 0;
512
                        if (!$error && $line->fk_warehouse > 0) {
513
                            // Record stock movement
514
                            $id_product_batch = 0;
515
                            $stockmove->origin_type = 'mo';
516
                            $stockmove->origin_id = $this->mo->id;
517
                            if ($qtytoprocess >= 0) {
518
                                $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
519
                            } else {
520
                                $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
521
                            }
522
                            if ($idstockmove < 0) {
523
                                $error++;
524
                                throw new RestException(500, $stockmove->error);
525
                            }
526
                        }
527
                        if (!$error) {
528
                            // Record consumption
529
                            $moline = new MoLine($this->db);
530
                            $moline->fk_mo = $this->mo->id;
531
                            $moline->position = $pos;
532
                            $moline->fk_product = $line->fk_product;
533
                            $moline->fk_warehouse = $line->fk_warehouse;
534
                            $moline->qty = $qtytoprocess;
535
                            $moline->batch = $tmpproduct->status_batch;
536
                            $moline->role = 'consumed';
537
                            $moline->fk_mrp_production = $line->id;
538
                            $moline->fk_stock_movement = $idstockmove;
539
                            $moline->fk_user_creat = DolibarrApiAccess::$user->id;
540
541
                            $resultmoline = $moline->create(DolibarrApiAccess::$user);
542
                            if ($resultmoline <= 0) {
543
                                $error++;
544
                                throw new RestException(500, $moline->error);
545
                            }
546
547
                            $pos++;
548
                        }
549
                    }
550
                }
551
            }
552
            $pos = 0;
553
            foreach ($this->mo->lines as $line) {
554
                if ($line->role == 'toproduce') {
555
                    $tmpproduct = new Product($this->db);
556
                    $tmpproduct->fetch($line->fk_product);
557
                    if ($line->qty != 0) {
558
                        $qtytoprocess = $line->qty;
559
                        if (isset($line->fk_warehouse)) {   // If there is a warehouse to set
560
                            if (!($line->fk_warehouse > 0)) {   // If there is no warehouse set.
561
                                $langs->load("errors");
562
                                $error++;
563
                                throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref));
564
                            }
565
                            if ($tmpproduct->status_batch) {
566
                                $langs->load("errors");
567
                                $error++;
568
                                throw new RestException(500, $langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref));
569
                            }
570
                        }
571
                        $idstockmove = 0;
572
                        if (!$error && $line->fk_warehouse > 0) {
573
                            // Record stock movement
574
                            $id_product_batch = 0;
575
                            $stockmove->origin_type = 'mo';
576
                            $stockmove->origin_id = $this->mo->id;
577
                            if ($qtytoprocess >= 0) {
578
                                $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
579
                            } else {
580
                                $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $line->fk_product, $line->fk_warehouse, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
581
                            }
582
                            if ($idstockmove < 0) {
583
                                $error++;
584
                                throw new RestException(500, $stockmove->error);
585
                            }
586
                        }
587
                        if (!$error) {
588
                            // Record consumption
589
                            $moline = new MoLine($this->db);
590
                            $moline->fk_mo = $this->mo->id;
591
                            $moline->position = $pos;
592
                            $moline->fk_product = $line->fk_product;
593
                            $moline->fk_warehouse = $line->fk_warehouse;
594
                            $moline->qty = $qtytoprocess;
595
                            $moline->batch = $tmpproduct->status_batch;
596
                            $moline->role = 'produced';
597
                            $moline->fk_mrp_production = $line->id;
598
                            $moline->fk_stock_movement = $idstockmove;
599
                            $moline->fk_user_creat = DolibarrApiAccess::$user->id;
600
601
                            $resultmoline = $moline->create(DolibarrApiAccess::$user);
602
                            if ($resultmoline <= 0) {
603
                                $error++;
604
                                throw new RestException(500, $moline->error);
605
                            }
606
607
                            $pos++;
608
                        }
609
                    }
610
                }
611
            }
612
613
            if (!$error) {
614
                if ($autoclose > 0) {
615
                    foreach ($this->mo->lines as $line) {
616
                        if ($line->role == 'toconsume') {
617
                            $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
618
                            $alreadyconsumed = 0;
619
                            foreach ($arrayoflines as $line2) {
620
                                $alreadyconsumed += $line2['qty'];
621
                            }
622
623
                            if ($alreadyconsumed < $line->qty) {
624
                                $consumptioncomplete = false;
625
                            }
626
                        }
627
                        if ($line->role == 'toproduce') {
628
                            $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
629
                            $alreadyproduced = 0;
630
                            foreach ($arrayoflines as $line2) {
631
                                $alreadyproduced += $line2['qty'];
632
                            }
633
634
                            if ($alreadyproduced < $line->qty) {
635
                                $productioncomplete = false;
636
                            }
637
                        }
638
                    }
639
                } else {
640
                    $consumptioncomplete = false;
641
                    $productioncomplete = false;
642
                }
643
            }
644
        }
645
646
        // Update status of MO
647
        dol_syslog("consumptioncomplete = " . json_encode($consumptioncomplete) . " productioncomplete = " . json_encode($productioncomplete));
648
        if ($consumptioncomplete && $productioncomplete) {
649
            $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
650
        } else {
651
            $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
652
        }
653
        if ($result <= 0) {
654
            throw new RestException(500, $this->mo->error);
655
        }
656
657
        return $this->mo->id;
658
    }
659
660
    /**
661
     * Produce and consume
662
     *
663
     * Example:
664
     * {
665
     *   "inventorylabel": "Produce and consume using API",
666
     *   "inventorycode": "PRODUCEAPI-YY-MM-DD",
667
     *   "autoclose": 1,
668
     *   "arraytoconsume": [
669
     *     {
670
     *       "objectid": "123",  -- rowid of MoLine
671
     *       "qty": "2",
672
     *       "fk_warehouse": "789"
673
     *     }
674
     *   ],
675
     *   "arraytoproduce": [
676
     *     {
677
     *       "objectid": "456",  -- rowid of MoLine
678
     *       "qty": "1",
679
     *       "fk_warehouse": "789",
680
     *       "pricetoproduce": "12.3"  -- optional
681
     *     }
682
     *   ]
683
     * }
684
     *
685
     * @param int       $id             ID of state
686
     * @param array     $request_data   Request datas
687
     *
688
     * @url     POST {id}/produceandconsume
689
     *
690
     * @return int  ID of MO
691
     */
692
    public function produceAndConsume($id, $request_data = null)
693
    {
694
        if (!DolibarrApiAccess::$user->hasRight("mrp", "write")) {
695
            throw new RestException(403, 'Not enough permission');
696
        }
697
        $result = $this->mo->fetch($id);
698
        if (!$result) {
699
            throw new RestException(404, 'MO not found');
700
        }
701
702
        if ($this->mo->status != Mo::STATUS_VALIDATED && $this->mo->status != Mo::STATUS_INPROGRESS) {
703
            throw new RestException(405, 'Error bad status of MO');
704
        }
705
706
        // Code for consume and produce...
707
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
708
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
709
        require_once constant('DOL_DOCUMENT_ROOT') . '/mrp/lib/mrp_mo.lib.php';
710
711
        $stockmove = new MouvementStock($this->db);
712
713
        $labelmovement = '';
714
        $codemovement = '';
715
        $autoclose = 1;
716
        $arraytoconsume = array();
717
        $arraytoproduce = array();
718
719
        foreach ($request_data as $field => $value) {
720
            if ($field == 'inventorylabel') {
721
                $labelmovement = $value;
722
            }
723
            if ($field == 'inventorycode') {
724
                $codemovement = $value;
725
            }
726
            if ($field == 'autoclose') {
727
                $autoclose = $value;
728
            }
729
            if ($field == 'arraytoconsume') {
730
                $arraytoconsume = $value;
731
            }
732
            if ($field == 'arraytoproduce') {
733
                $arraytoproduce = $value;
734
            }
735
            if ($field === 'caller') {
736
                // 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
737
                $stockmove->context['caller'] = $request_data['caller'];
738
                continue;
739
            }
740
        }
741
742
        if (empty($labelmovement)) {
743
            throw new RestException(500, "Field inventorylabel not provided");
744
        }
745
        if (empty($codemovement)) {
746
            throw new RestException(500, "Field inventorycode not provided");
747
        }
748
749
        $this->db->begin();
750
751
        $pos = 0;
752
        $arrayofarrayname = array("arraytoconsume","arraytoproduce");
753
        foreach ($arrayofarrayname as $arrayname) {
754
            foreach (${$arrayname} as $value) {
755
                if (empty($value["objectid"])) {
756
                    throw new RestException(500, "Field objectid required in " . $arrayname);
757
                }
758
759
                $molinetoprocess = new MoLine($this->db);
760
                $tmpmolineid = $molinetoprocess->fetch($value["objectid"]);
761
                if ($tmpmolineid <= 0) {
762
                    throw new RestException(500, "MoLine with rowid " . $value["objectid"] . " not exist.");
763
                }
764
765
                $tmpproduct = new Product($this->db);
766
                $tmpproduct->fetch($molinetoprocess->fk_product);
767
                if ($tmpproduct->status_batch) {
768
                    throw new RestException(500, "Product " . $tmpproduct->ref . " must be in batch, this API can't handle it currently.");
769
                }
770
771
                if (empty($value["qty"]) && $value["qty"] != 0) {
772
                    throw new RestException(500, "Field qty with lower or higher then 0 required in " . $arrayname);
773
                }
774
                $qtytoprocess = $value["qty"];
775
776
                if (isset($value["fk_warehouse"])) {    // If there is a warehouse to set
777
                    if (!($value["fk_warehouse"] > 0)) {    // If there is no warehouse set.
778
                        throw new RestException(500, "Field fk_warehouse required in " . $arrayname);
779
                    }
780
                }
781
                $fk_warehousetoprocess = $value["fk_warehouse"];
782
783
                $pricetoproduce = 0;
784
                if (isset($value["pricetoproduce"])) {    // If there is a price to produce set.
785
                    if ($value["pricetoproduce"] > 0) {    // Only use prices grater then 0.
786
                        $pricetoproduce = $value["pricetoproduce"];
787
                    }
788
                }
789
790
                $idstockmove = 0;
791
792
                // Record stock movement
793
                $id_product_batch = 0;
794
                $stockmove->origin_type = 'mo';
795
                $stockmove->origin_id = $this->mo->id;
796
                if ($arrayname == "arraytoconsume") {
797
                    if ($qtytoprocess >= 0) {
798
                        $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
799
                    } else {
800
                        $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
801
                    }
802
                } else {
803
                    if ($qtytoprocess >= 0) {
804
                        $idstockmove = $stockmove->reception(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, $pricetoproduce, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
805
                    } else {
806
                        $idstockmove = $stockmove->livraison(DolibarrApiAccess::$user, $molinetoprocess->fk_product, $fk_warehousetoprocess, $qtytoprocess, 0, $labelmovement, dol_now(), '', '', $tmpproduct->status_batch, $id_product_batch, $codemovement);
807
                    }
808
                }
809
                if ($idstockmove <= 0) {
810
                    throw new RestException(500, $stockmove->error);
811
                }
812
813
                // Record consumption
814
                $moline = new MoLine($this->db);
815
                $moline->fk_mo = $this->mo->id;
816
                $moline->position = $pos;
817
                $moline->fk_product = $tmpproduct->id;
818
                $moline->fk_warehouse = $fk_warehousetoprocess;
819
                $moline->qty = $qtytoprocess;
820
                $moline->batch = '';
821
                $moline->fk_mrp_production = $molinetoprocess->id;
822
                $moline->fk_stock_movement = $idstockmove;
823
                $moline->fk_user_creat = DolibarrApiAccess::$user->id;
824
825
                if ($arrayname == "arraytoconsume") {
826
                    $moline->role = 'consumed';
827
                } else {
828
                    $moline->role = 'produced';
829
                }
830
831
                $resultmoline = $moline->create(DolibarrApiAccess::$user);
832
                if ($resultmoline <= 0) {
833
                    throw new RestException(500, $moline->error);
834
                }
835
836
                $pos++;
837
            }
838
        }
839
840
        $consumptioncomplete = true;
841
        $productioncomplete = true;
842
843
        if ($autoclose > 0) {
844
            // Refresh Lines after consumptions.
845
            $this->mo->fetchLines();
846
847
            foreach ($this->mo->lines as $line) {
848
                if ($line->role == 'toconsume') {
849
                    $arrayoflines = $this->mo->fetchLinesLinked('consumed', $line->id);
850
                    $alreadyconsumed = 0;
851
                    foreach ($arrayoflines as $line2) {
852
                        $alreadyconsumed += $line2['qty'];
853
                    }
854
855
                    if ($alreadyconsumed < $line->qty) {
856
                        $consumptioncomplete = false;
857
                    }
858
                }
859
                if ($line->role == 'toproduce') {
860
                    $arrayoflines = $this->mo->fetchLinesLinked('produced', $line->id);
861
                    $alreadyproduced = 0;
862
                    foreach ($arrayoflines as $line2) {
863
                        $alreadyproduced += $line2['qty'];
864
                    }
865
866
                    if ($alreadyproduced < $line->qty) {
867
                        $productioncomplete = false;
868
                    }
869
                }
870
            }
871
        } else {
872
            $consumptioncomplete = false;
873
            $productioncomplete = false;
874
        }
875
876
        // Update status of MO
877
        dol_syslog("consumptioncomplete = " . (string) $consumptioncomplete . " productioncomplete = " . (string) $productioncomplete);
878
        //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
879
        if ($consumptioncomplete && $productioncomplete) {
880
            $result = $this->mo->setStatut(Mo::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
881
        } else {
882
            $result = $this->mo->setStatut(Mo::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
883
        }
884
        if ($result <= 0) {
885
            throw new RestException(500, $this->mo->error);
886
        }
887
888
        $this->db->commit();
889
        return $this->mo->id;
890
    }
891
892
893
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
894
    /**
895
     * Clean sensible object datas
896
     *
897
     * @param   Object  $object         Object to clean
898
     * @return  Object                  Object with cleaned properties
899
     */
900
    protected function _cleanObjectDatas($object)
901
    {
902
		// phpcs:enable
903
        $object = parent::_cleanObjectDatas($object);
904
905
        unset($object->rowid);
906
        unset($object->canvas);
907
908
        unset($object->name);
909
        unset($object->lastname);
910
        unset($object->firstname);
911
        unset($object->civility_id);
912
        unset($object->statut);
913
        unset($object->state);
914
        unset($object->state_id);
915
        unset($object->state_code);
916
        unset($object->region);
917
        unset($object->region_code);
918
        unset($object->country);
919
        unset($object->country_id);
920
        unset($object->country_code);
921
        unset($object->barcode_type);
922
        unset($object->barcode_type_code);
923
        unset($object->barcode_type_label);
924
        unset($object->barcode_type_coder);
925
        unset($object->total_ht);
926
        unset($object->total_tva);
927
        unset($object->total_localtax1);
928
        unset($object->total_localtax2);
929
        unset($object->total_ttc);
930
        unset($object->fk_account);
931
        unset($object->comments);
932
        unset($object->note);
933
        unset($object->mode_reglement_id);
934
        unset($object->cond_reglement_id);
935
        unset($object->cond_reglement);
936
        unset($object->shipping_method_id);
937
        unset($object->fk_incoterms);
938
        unset($object->label_incoterms);
939
        unset($object->location_incoterms);
940
941
        // If object has lines, remove $db property
942
        if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
943
            $nboflines = count($object->lines);
944
            for ($i = 0; $i < $nboflines; $i++) {
945
                $this->_cleanObjectDatas($object->lines[$i]);
946
947
                unset($object->lines[$i]->lines);
948
                unset($object->lines[$i]->note);
949
            }
950
        }
951
952
        return $object;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $object also could return the type object which is incompatible with the documented return type object.
Loading history...
953
    }
954
955
    /**
956
     * Validate fields before create or update object
957
     *
958
     * @param   array       $data   Array of data to validate
959
     * @return  array
960
     *
961
     * @throws  RestException
962
     */
963
    private function _validate($data)
964
    {
965
        $myobject = array();
966
        foreach ($this->mo->fields as $field => $propfield) {
967
            if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || $propfield['notnull'] != 1) {
968
                continue; // Not a mandatory field
969
            }
970
            if (!isset($data[$field])) {
971
                throw new RestException(400, "$field field missing");
972
            }
973
            $myobject[$field] = $data[$field];
974
        }
975
        return $myobject;
976
    }
977
978
    /**
979
     * Validate the ref field and get the next Number if it's necessary.
980
     *
981
     * @return void
982
     */
983
    private function checkRefNumbering()
984
    {
985
        $ref = substr($this->mo->ref, 1, 4);
986
        if ($this->mo->status > 0 && $ref == 'PROV') {
987
            throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
988
        }
989
990
        if (strtolower($this->mo->ref) == 'auto') {
991
            if (empty($this->mo->id) && $this->mo->status == 0) {
992
                $this->mo->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
993
            } else {
994
                $this->mo->fetch_product();
995
                $numref = $this->mo->getNextNumRef($this->mo->product);
996
                $this->mo->ref = $numref;
997
            }
998
        }
999
    }
1000
}
1001