Passed
Branch develop (4336b1)
by
unknown
85:39
created

Boms::checkRefNumbering()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
nc 4
nop 0
dl 0
loc 14
rs 9.2222
c 2
b 0
f 0
1
<?php
2
/* Copyright (C) 2015   Jean-François Ferry     <[email protected]>
3
 * Copyright (C) 2019 Maxime Kohlhaas <[email protected]>
4
 * Copyright (C) 2020     	Frédéric France		<[email protected]>
5
 * Copyright (C) 2022     	Christian Humpel		<[email protected]>
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
use Luracast\Restler\RestException;
22
23
require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
24
25
26
/**
27
 * \file    bom/class/api_boms.class.php
28
 * \ingroup bom
29
 * \brief   File for API management of BOM.
30
 */
31
32
/**
33
 * API class for BOM
34
 *
35
 * @access protected
36
 * @class  DolibarrApiAccess {@requires user,external}
37
 */
38
class Boms extends DolibarrApi
39
{
40
	/**
41
	 * @var BOM $bom {@type BOM}
42
	 */
43
	public $bom;
44
45
	/**
46
	 * Constructor
47
	 */
48
	public function __construct()
49
	{
50
		global $db, $conf;
51
		$this->db = $db;
52
		$this->bom = new BOM($this->db);
53
	}
54
55
	/**
56
	 * Get properties of a bom object
57
	 *
58
	 * Return an array with bom informations
59
	 *
60
	 * @param 	int 	$id ID of bom
61
	 * @return 	array|mixed data without useless information
62
	 *
63
	 * @url	GET {id}
64
	 * @throws 	RestException
65
	 */
66
	public function get($id)
67
	{
68
		if (!DolibarrApiAccess::$user->rights->bom->read) {
69
			throw new RestException(401);
70
		}
71
72
		$result = $this->bom->fetch($id);
73
		if (!$result) {
74
			throw new RestException(404, 'BOM not found');
75
		}
76
77
		if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
78
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
79
		}
80
81
		return $this->_cleanObjectDatas($this->bom);
82
	}
83
84
85
	/**
86
	 * List boms
87
	 *
88
	 * Get a list of boms
89
	 *
90
	 * @param string	       $sortfield	        Sort field
91
	 * @param string	       $sortorder	        Sort order
92
	 * @param int		       $limit		        Limit for list
93
	 * @param int		       $page		        Page number
94
	 * @param string           $sqlfilters          Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
95
	 * @return  array                               Array of order objects
96
	 *
97
	 * @throws RestException
98
	 */
99
	public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '')
100
	{
101
		global $db, $conf;
102
103
		if (!DolibarrApiAccess::$user->rights->bom->read) {
104
			throw new RestException(401);
105
		}
106
107
		$obj_ret = array();
108
		$tmpobject = new BOM($this->db);
109
110
		$socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
111
112
		$restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
113
114
		// If the internal user must only see his customers, force searching by him
115
		$search_sale = 0;
116
		if ($restrictonsocid && !DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) {
117
			$search_sale = DolibarrApiAccess::$user->id;
118
		}
119
120
		$sql = "SELECT t.rowid";
121
		if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
122
			$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)
123
		}
124
		$sql .= " FROM ".MAIN_DB_PREFIX.$tmpobject->table_element." as t";
125
126
		if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
127
			$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
128
		}
129
		$sql .= " WHERE 1 = 1";
130
131
		// Example of use $mode
132
		//if ($mode == 1) $sql.= " AND s.client IN (1, 3)";
133
		//if ($mode == 2) $sql.= " AND s.client IN (2, 3)";
134
135
		if ($tmpobject->ismultientitymanaged) {
136
			$sql .= ' AND t.entity IN ('.getEntity($tmpobject->element).')';
137
		}
138
		if ($restrictonsocid && (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
139
			$sql .= " AND t.fk_soc = sc.fk_soc";
140
		}
141
		if ($restrictonsocid && $socid) {
142
			$sql .= " AND t.fk_soc = ".((int) $socid);
143
		}
144
		if ($restrictonsocid && $search_sale > 0) {
145
			$sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
146
		}
147
		// Insert sale filter
148
		if ($restrictonsocid && $search_sale > 0) {
149
			$sql .= " AND sc.fk_user = ".((int) $search_sale);
150
		}
151
		if ($sqlfilters) {
152
			$errormessage = '';
153
			if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
154
				throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
155
			}
156
			$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
157
			$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
158
		}
159
160
		$sql .= $this->db->order($sortfield, $sortorder);
161
		if ($limit) {
162
			if ($page < 0) {
163
				$page = 0;
164
			}
165
			$offset = $limit * $page;
166
167
			$sql .= $this->db->plimit($limit + 1, $offset);
168
		}
169
170
		$result = $this->db->query($sql);
171
		if ($result) {
172
			$num = $this->db->num_rows($result);
173
			$i = 0;
174
			while ($i < $num) {
175
				$obj = $this->db->fetch_object($result);
176
				$bom_static = new BOM($this->db);
177
				if ($bom_static->fetch($obj->rowid)) {
178
					$obj_ret[] = $this->_cleanObjectDatas($bom_static);
179
				}
180
				$i++;
181
			}
182
		} else {
183
			throw new RestException(503, 'Error when retrieve bom list');
184
		}
185
		if (!count($obj_ret)) {
186
			throw new RestException(404, 'No bom found');
187
		}
188
		return $obj_ret;
189
	}
190
191
	/**
192
	 * Create bom object
193
	 *
194
	 * @param array $request_data   Request datas
195
	 * @return int  ID of bom
196
	 */
197
	public function post($request_data = null)
198
	{
199
		if (!DolibarrApiAccess::$user->rights->bom->write) {
200
			throw new RestException(401);
201
		}
202
		// Check mandatory fields
203
		$result = $this->_validate($request_data);
204
205
		foreach ($request_data as $field => $value) {
206
			$this->bom->$field = $value;
207
		}
208
209
		$this->checkRefNumbering();
210
211
		if (!$this->bom->create(DolibarrApiAccess::$user)) {
212
			throw new RestException(500, "Error creating BOM", array_merge(array($this->bom->error), $this->bom->errors));
213
		}
214
		return $this->bom->id;
215
	}
216
217
	/**
218
	 * Update bom
219
	 *
220
	 * @param int   $id             Id of bom to update
221
	 * @param array $request_data   Datas
222
	 *
223
	 * @return int
224
	 */
225
	public function put($id, $request_data = null)
226
	{
227
		if (!DolibarrApiAccess::$user->rights->bom->write) {
228
			throw new RestException(401);
229
		}
230
231
		$result = $this->bom->fetch($id);
232
		if (!$result) {
233
			throw new RestException(404, 'BOM not found');
234
		}
235
236
		if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
237
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
238
		}
239
240
		foreach ($request_data as $field => $value) {
241
			if ($field == 'id') {
242
				continue;
243
			}
244
			$this->bom->$field = $value;
245
		}
246
247
		$this->checkRefNumbering();
248
249
		if ($this->bom->update(DolibarrApiAccess::$user) > 0) {
250
			return $this->get($id);
251
		} else {
252
			throw new RestException(500, $this->bom->error);
253
		}
254
	}
255
256
	/**
257
	 * Delete bom
258
	 *
259
	 * @param   int     $id   BOM ID
260
	 * @return  array
261
	 */
262
	public function delete($id)
263
	{
264
		if (!DolibarrApiAccess::$user->rights->bom->delete) {
265
			throw new RestException(401);
266
		}
267
		$result = $this->bom->fetch($id);
268
		if (!$result) {
269
			throw new RestException(404, 'BOM not found');
270
		}
271
272
		if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
273
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
274
		}
275
276
		if (!$this->bom->delete(DolibarrApiAccess::$user)) {
277
			throw new RestException(500, 'Error when deleting BOM : '.$this->bom->error);
278
		}
279
280
		return array(
281
			'success' => array(
282
				'code' => 200,
283
				'message' => 'BOM deleted'
284
			)
285
		);
286
	}
287
288
	/**
289
	 * Get lines of an BOM
290
	 *
291
	 * @param int   $id             Id of BOM
292
	 *
293
	 * @url	GET {id}/lines
294
	 *
295
	 * @return array
296
	 */
297
	public function getLines($id)
298
	{
299
		if (!DolibarrApiAccess::$user->rights->bom->read) {
300
			throw new RestException(401);
301
		}
302
303
		$result = $this->bom->fetch($id);
304
		if (!$result) {
305
			throw new RestException(404, 'BOM not found');
306
		}
307
308
		if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
309
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
310
		}
311
		$this->bom->getLinesArray();
312
		$result = array();
313
		foreach ($this->bom->lines as $line) {
314
			array_push($result, $this->_cleanObjectDatas($line));
315
		}
316
		return $result;
317
	}
318
319
	/**
320
	 * Add a line to given BOM
321
	 *
322
	 * @param int   $id             Id of BOM to update
323
	 * @param array $request_data   BOMLine data
324
	 *
325
	 * @url	POST {id}/lines
326
	 *
327
	 * @return int
328
	 */
329
	public function postLine($id, $request_data = null)
330
	{
331
		if (!DolibarrApiAccess::$user->rights->bom->write) {
332
			throw new RestException(401);
333
		}
334
335
		$result = $this->bom->fetch($id);
336
		if (!$result) {
337
			throw new RestException(404, 'BOM not found');
338
		}
339
340
		if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
341
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
342
		}
343
344
		$request_data = (object) $request_data;
345
346
		$updateRes = $this->bom->addLine(
347
			$request_data->fk_product,
348
			$request_data->qty,
349
			$request_data->qty_frozen,
350
			$request_data->disable_stock_change,
351
			$request_data->efficiency,
352
			$request_data->position,
353
			$request_data->fk_bom_child,
354
			$request_data->import_key
355
		);
356
357
		if ($updateRes > 0) {
358
			return $updateRes;
359
		} else {
360
			throw new RestException(400, $this->bom->error);
361
		}
362
	}
363
364
	/**
365
	 * Update a line to given BOM
366
	 *
367
	 * @param int   $id             Id of BOM to update
368
	 * @param int   $lineid         Id of line to update
369
	 * @param array $request_data   BOMLine data
370
	 *
371
	 * @url	PUT {id}/lines/{lineid}
372
	 *
373
	 * @return array|bool
374
	 */
375
	public function putLine($id, $lineid, $request_data = null)
376
	{
377
		if (!DolibarrApiAccess::$user->rights->bom->write) {
378
			throw new RestException(401);
379
		}
380
381
		$result = $this->bom->fetch($id);
382
		if (!$result) {
383
			throw new RestException(404, 'BOM not found');
384
		}
385
386
		if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
387
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
388
		}
389
390
		$request_data = (object) $request_data;
391
392
		$updateRes = $this->bom->updateLine(
393
			$lineid,
394
			$request_data->qty,
395
			$request_data->qty_frozen,
396
			$request_data->disable_stock_change,
397
			$request_data->efficiency,
398
			$request_data->position,
399
			$request_data->import_key
400
		);
401
402
		if ($updateRes > 0) {
403
			$result = $this->get($id);
404
			unset($result->line);
405
			return $this->_cleanObjectDatas($result);
406
		}
407
		return false;
408
	}
409
410
	/**
411
	 * Delete a line to given BOM
412
	 *
413
	 *
414
	 * @param int   $id             Id of BOM to update
415
	 * @param int   $lineid         Id of line to delete
416
	 *
417
	 * @url	DELETE {id}/lines/{lineid}
418
	 *
419
	 * @return int
420
	 *
421
	 * @throws RestException 401
422
	 * @throws RestException 404
423
	 * @throws RestException 500
424
	 */
425
	public function deleteLine($id, $lineid)
426
	{
427
		if (!DolibarrApiAccess::$user->rights->bom->write) {
428
			throw new RestException(401);
429
		}
430
431
		$result = $this->bom->fetch($id);
432
		if (!$result) {
433
			throw new RestException(404, 'BOM not found');
434
		}
435
436
		if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
437
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
438
		}
439
440
		//Check the rowid is a line of current bom object
441
		$lineIdIsFromObject = false;
442
		foreach ($this->bom->lines as $bl) {
443
			if ($bl->id == $lineid) {
444
				$lineIdIsFromObject = true;
445
				break;
446
			}
447
		}
448
		if (!$lineIdIsFromObject) {
449
			throw new RestException(500, 'Line to delete (rowid: '.$lineid.') is not a line of BOM (id: '.$this->bom->id.')');
450
		}
451
452
		$updateRes = $this->bom->deleteline(DolibarrApiAccess::$user, $lineid);
453
		if ($updateRes > 0) {
454
			return $this->get($id);
455
		} else {
456
			throw new RestException(405, $this->bom->error);
457
		}
458
	}
459
460
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
461
	/**
462
	 * Clean sensible object datas
463
	 *
464
	 * @param   Object  $object     Object to clean
465
	 * @return  Object              Object with cleaned properties
466
	 */
467
	protected function _cleanObjectDatas($object)
468
	{
469
		// phpcs:enable
470
		$object = parent::_cleanObjectDatas($object);
471
472
		unset($object->rowid);
473
		unset($object->canvas);
474
475
		unset($object->name);
476
		unset($object->lastname);
477
		unset($object->firstname);
478
		unset($object->civility_id);
479
		unset($object->statut);
480
		unset($object->state);
481
		unset($object->state_id);
482
		unset($object->state_code);
483
		unset($object->region);
484
		unset($object->region_code);
485
		unset($object->country);
486
		unset($object->country_id);
487
		unset($object->country_code);
488
		unset($object->barcode_type);
489
		unset($object->barcode_type_code);
490
		unset($object->barcode_type_label);
491
		unset($object->barcode_type_coder);
492
		unset($object->total_ht);
493
		unset($object->total_tva);
494
		unset($object->total_localtax1);
495
		unset($object->total_localtax2);
496
		unset($object->total_ttc);
497
		unset($object->fk_account);
498
		unset($object->comments);
499
		unset($object->note);
500
		unset($object->mode_reglement_id);
501
		unset($object->cond_reglement_id);
502
		unset($object->cond_reglement);
503
		unset($object->shipping_method_id);
504
		unset($object->fk_incoterms);
505
		unset($object->label_incoterms);
506
		unset($object->location_incoterms);
507
508
		// If object has lines, remove $db property
509
		if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
510
			$nboflines = count($object->lines);
511
			for ($i = 0; $i < $nboflines; $i++) {
512
				$this->_cleanObjectDatas($object->lines[$i]);
513
514
				unset($object->lines[$i]->lines);
515
				unset($object->lines[$i]->note);
516
			}
517
		}
518
519
		return $object;
520
	}
521
522
	/**
523
	 * Validate fields before create or update object
524
	 *
525
	 * @param	array		$data   Array of data to validate
526
	 * @return	array
527
	 *
528
	 * @throws	RestException
529
	 */
530
	private function _validate($data)
531
	{
532
		$myobject = array();
533
		foreach ($this->bom->fields as $field => $propfield) {
534
			if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || $propfield['notnull'] != 1) {
535
				continue; // Not a mandatory field
536
			}
537
			if (!isset($data[$field])) {
538
				throw new RestException(400, "$field field missing");
539
			}
540
				$myobject[$field] = $data[$field];
541
		}
542
		return $myobject;
543
	}
544
545
	/**
546
	 * Validate the ref field and get the next Number if it's necessary.
547
	 *
548
	 * @return void
549
	 */
550
	private function checkRefNumbering(): void
551
	{
552
		$ref = substr($this->bom->ref, 1, 4);
553
		if ($this->bom->status > 0 && $ref == 'PROV') {
554
			throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
555
		}
556
557
		if (strtolower($this->bom->ref) == 'auto') {
558
			if (empty($this->bom->id) && $this->bom->status == 0) {
559
				$this->bom->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
560
			} else {
561
				$this->bom->fetch_product();
562
				$numref = $this->bom->getNextNumRef($this->bom->product);
563
				$this->bom->ref = $numref;
564
			}
565
		}
566
	}
567
}
568