Passed
Push — EXTRACT_CLASSES ( 0382f2...c25e41 )
by Rafael
52:18
created

Contracts::putLine()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 48
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 34
nc 5
nop 3
dl 0
loc 48
rs 8.7537
c 0
b 0
f 0
1
<?php
2
3
/* Copyright (C) 2015       Jean-François Ferry         <[email protected]>
4
 * Copyright (C) 2016		Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2018-2020  Frédéric France             <[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\Contrat\Api;
23
24
use Luracast\Restler\RestException;
25
26
require_once constant('DOL_DOCUMENT_ROOT') . '/contrat/class/contrat.class.php';
27
28
/**
29
 * API class for contracts
30
 *
31
 * @access protected
32
 * @class  DolibarrApiAccess {@requires user,external}
33
 */
34
class Contracts extends DolibarrApi
35
{
36
    /**
37
     * @var array $FIELDS Mandatory fields, checked when create and update object
38
     */
39
    public static $FIELDS = array(
40
        'socid',
41
        'date_contrat',
42
        'commercial_signature_id',
43
        'commercial_suivi_id'
44
    );
45
46
    /**
47
     * @var Contrat $contract {@type Contrat}
48
     */
49
    public $contract;
50
51
    /**
52
     * Constructor
53
     */
54
    public function __construct()
55
    {
56
        global $db, $conf;
57
        $this->db = $db;
58
        $this->contract = new Contrat($this->db);
59
    }
60
61
    /**
62
     * List contracts
63
     *
64
     * Get a list of contracts
65
     *
66
     * @param string $sortfield Sort field
67
     * @param string $sortorder Sort order
68
     * @param int $limit Limit for list
69
     * @param int $page Page number
70
     * @param string $thirdparty_ids Thirdparty ids to filter contracts of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
71
     * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
72
     * @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
73
     * @return  array                               Array of contract objects
74
     *
75
     * @throws RestException 404 Not found
76
     * @throws RestException 503 Error
77
     */
78
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $sqlfilters = '', $properties = '')
79
    {
80
        global $db, $conf;
81
82
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
83
            throw new RestException(403);
84
        }
85
86
        $obj_ret = array();
87
88
        // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
89
        $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
90
91
        // If the internal user must only see his customers, force searching by him
92
        $search_sale = 0;
93
        if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
94
            $search_sale = DolibarrApiAccess::$user->id;
95
        }
96
97
        $sql = "SELECT t.rowid";
98
        $sql .= " FROM " . MAIN_DB_PREFIX . "contrat AS t LEFT JOIN " . MAIN_DB_PREFIX . "contrat_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
99
        $sql .= ' WHERE t.entity IN (' . getEntity('contrat') . ')';
100
        if ($socids) {
101
            $sql .= " AND t.fk_soc IN (" . $this->db->sanitize($socids) . ")";
102
        }
103
        // Search on sale representative
104
        if ($search_sale && $search_sale != '-1') {
105
            if ($search_sale == -2) {
106
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
107
            } elseif ($search_sale > 0) {
108
                $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) . ")";
109
            }
110
        }
111
        // Add sql filters
112
        if ($sqlfilters) {
113
            $errormessage = '';
114
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
115
            if ($errormessage) {
116
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
117
            }
118
        }
119
120
        $sql .= $this->db->order($sortfield, $sortorder);
121
        if ($limit) {
122
            if ($page < 0) {
123
                $page = 0;
124
            }
125
            $offset = $limit * $page;
126
127
            $sql .= $this->db->plimit($limit + 1, $offset);
128
        }
129
130
        dol_syslog("API Rest request");
131
        $result = $this->db->query($sql);
132
133
        if ($result) {
134
            $num = $this->db->num_rows($result);
135
            $min = min($num, ($limit <= 0 ? $num : $limit));
136
            $i = 0;
137
            while ($i < $min) {
138
                $obj = $this->db->fetch_object($result);
139
                $contrat_static = new Contrat($this->db);
140
                if ($contrat_static->fetch($obj->rowid)) {
141
                    $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($contrat_static), $properties);
142
                }
143
                $i++;
144
            }
145
        } else {
146
            throw new RestException(503, 'Error when retrieve contrat list : ' . $this->db->lasterror());
147
        }
148
149
        return $obj_ret;
150
    }
151
152
    /**
153
     * Clean sensible object datas
154
     *
155
     * @param Object $object Object to clean
156
     * @return  Object              Object with cleaned properties
157
     */
158
    protected function _cleanObjectDatas($object)
159
    {
160
        // phpcs:enable
161
        $object = parent::_cleanObjectDatas($object);
162
163
        unset($object->address);
164
        unset($object->civility_id);
165
166
        return $object;
167
    }
168
169
    /**
170
     * Create contract object
171
     *
172
     * @param array $request_data Request data
173
     * @return  int     ID of contrat
174
     */
175
    public function post($request_data = null)
176
    {
177
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
178
            throw new RestException(403, "Insufficient rights");
179
        }
180
        // Check mandatory fields
181
        $result = $this->_validate($request_data);
182
183
        foreach ($request_data as $field => $value) {
184
            if ($field === 'caller') {
185
                // 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
186
                $this->contract->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
187
                continue;
188
            }
189
190
            $this->contract->$field = $this->_checkValForAPI($field, $value, $this->contract);
191
        }
192
        /*if (isset($request_data["lines"])) {
193
          $lines = array();
194
          foreach ($request_data["lines"] as $line) {
195
            array_push($lines, (object) $line);
196
          }
197
          $this->contract->lines = $lines;
198
        }*/
199
        if ($this->contract->create(DolibarrApiAccess::$user) < 0) {
200
            throw new RestException(500, "Error creating contract", array_merge(array($this->contract->error), $this->contract->errors));
201
        }
202
203
        return $this->contract->id;
204
    }
205
206
    /**
207
     * Validate fields before create or update object
208
     *
209
     * @param array $data Array with data to verify
210
     * @return  array
211
     * @throws  RestException
212
     */
213
    private function _validate($data)
214
    {
215
        $contrat = array();
216
        foreach (Contracts::$FIELDS as $field) {
217
            if (!isset($data[$field])) {
218
                throw new RestException(400, "$field field missing");
219
            }
220
            $contrat[$field] = $data[$field];
221
        }
222
        return $contrat;
223
    }
224
225
    /**
226
     * Get lines of a contract
227
     *
228
     * @param int $id Id of contract
229
     *
230
     * @url GET {id}/lines
231
     *
232
     * @return array
233
     */
234
    public function getLines($id)
235
    {
236
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
237
            throw new RestException(403);
238
        }
239
240
        $result = $this->contract->fetch($id);
241
        if (!$result) {
242
            throw new RestException(404, 'Contract not found');
243
        }
244
245
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
246
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
247
        }
248
        $this->contract->getLinesArray();
249
        $result = array();
250
        foreach ($this->contract->lines as $line) {
251
            array_push($result, $this->_cleanObjectDatas($line));
252
        }
253
        return $result;
254
    }
255
256
    /**
257
     * Add a line to given contract
258
     *
259
     * @param int $id Id of contrat to update
260
     * @param array $request_data Contractline data
261
     *
262
     * @url POST {id}/lines
263
     *
264
     * @return int|bool
265
     */
266
    public function postLine($id, $request_data = null)
267
    {
268
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
269
            throw new RestException(403);
270
        }
271
272
        $result = $this->contract->fetch($id);
273
        if (!$result) {
274
            throw new RestException(404, 'Contract not found');
275
        }
276
277
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
278
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
279
        }
280
281
        $request_data = (object)$request_data;
282
283
        $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
284
        $request_data->price_base_type = sanitizeVal($request_data->price_base_type);
285
286
        $updateRes = $this->contract->addline(
287
            $request_data->desc,
288
            $request_data->subprice,
289
            $request_data->qty,
290
            $request_data->tva_tx,
291
            $request_data->localtax1_tx,
292
            $request_data->localtax2_tx,
293
            $request_data->fk_product,
294
            $request_data->remise_percent,
295
            $request_data->date_start,
296
            $request_data->date_end,
297
            $request_data->price_base_type ? $request_data->price_base_type : 'HT',
298
            $request_data->subprice_excl_tax,
299
            $request_data->info_bits,
300
            $request_data->fk_fournprice,
301
            $request_data->pa_ht,
302
            $request_data->array_options,
303
            $request_data->fk_unit,
304
            $request_data->rang
305
        );
306
307
        if ($updateRes > 0) {
308
            return $updateRes;
309
        }
310
        return false;
311
    }
312
313
    /**
314
     * Update a line to given contract
315
     *
316
     * @param int $id Id of contrat to update
317
     * @param int $lineid Id of line to update
318
     * @param array $request_data Contractline data
319
     *
320
     * @url PUT {id}/lines/{lineid}
321
     *
322
     * @return Object|bool
323
     */
324
    public function putLine($id, $lineid, $request_data = null)
325
    {
326
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
327
            throw new RestException(403);
328
        }
329
330
        $result = $this->contract->fetch($id);
331
        if (!$result) {
332
            throw new RestException(404, 'Contrat not found');
333
        }
334
335
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
336
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
337
        }
338
339
        $request_data = (object)$request_data;
340
341
        $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
342
        $request_data->price_base_type = sanitizeVal($request_data->price_base_type);
343
344
        $updateRes = $this->contract->updateline(
345
            $lineid,
346
            $request_data->desc,
347
            $request_data->subprice,
348
            $request_data->qty,
349
            $request_data->remise_percent,
350
            $request_data->date_start,
351
            $request_data->date_end,
352
            $request_data->tva_tx,
353
            $request_data->localtax1_tx,
354
            $request_data->localtax2_tx,
355
            $request_data->date_start_real,
356
            $request_data->date_end_real,
357
            $request_data->price_base_type ? $request_data->price_base_type : 'HT',
358
            $request_data->info_bits,
359
            $request_data->fk_fourn_price,
360
            $request_data->pa_ht,
361
            $request_data->array_options,
362
            $request_data->fk_unit
363
        );
364
365
        if ($updateRes > 0) {
366
            $result = $this->get($id);
367
            unset($result->line);
368
            return $this->_cleanObjectDatas($result);
369
        }
370
371
        return false;
372
    }
373
374
    /**
375
     * Get properties of a contract object
376
     *
377
     * Return an array with contract information
378
     *
379
     * @param int $id ID of contract
380
     * @return  Object                  Object with cleaned properties
381
     * @throws  RestException
382
     */
383
    public function get($id)
384
    {
385
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
386
            throw new RestException(403);
387
        }
388
389
        $result = $this->contract->fetch($id);
390
        if (!$result) {
391
            throw new RestException(404, 'Contract not found');
392
        }
393
394
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
395
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
396
        }
397
398
        $this->contract->fetchObjectLinked();
399
        return $this->_cleanObjectDatas($this->contract);
400
    }
401
402
    /**
403
     * Activate a service line of a given contract
404
     *
405
     * @param int $id Id of contract to activate
406
     * @param int $lineid Id of line to activate
407
     * @param string $datestart {@from body}  Date start        {@type timestamp}
408
     * @param string $dateend {@from body}  Date end          {@type timestamp}
409
     * @param string $comment {@from body}  Comment
410
     *
411
     * @url PUT {id}/lines/{lineid}/activate
412
     *
413
     * @return Object|bool
414
     */
415
    public function activateLine($id, $lineid, $datestart, $dateend = null, $comment = null)
416
    {
417
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
418
            throw new RestException(403);
419
        }
420
421
        $result = $this->contract->fetch($id);
422
        if (!$result) {
423
            throw new RestException(404, 'Contrat not found');
424
        }
425
426
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
427
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
428
        }
429
430
        $updateRes = $this->contract->active_line(DolibarrApiAccess::$user, $lineid, $datestart, $dateend, $comment);
431
432
        if ($updateRes > 0) {
433
            $result = $this->get($id);
434
            unset($result->line);
435
            return $this->_cleanObjectDatas($result);
436
        }
437
438
        return false;
439
    }
440
441
    /**
442
     * Unactivate a service line of a given contract
443
     *
444
     * @param int $id Id of contract to activate
445
     * @param int $lineid Id of line to activate
446
     * @param string $datestart {@from body}  Date start        {@type timestamp}
447
     * @param string $comment {@from body}  Comment
448
     *
449
     * @url PUT {id}/lines/{lineid}/unactivate
450
     *
451
     * @return Object|bool
452
     */
453
    public function unactivateLine($id, $lineid, $datestart, $comment = null)
454
    {
455
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
456
            throw new RestException(403);
457
        }
458
459
        $result = $this->contract->fetch($id);
460
        if (!$result) {
461
            throw new RestException(404, 'Contrat not found');
462
        }
463
464
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
465
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
466
        }
467
468
        $updateRes = $this->contract->close_line(DolibarrApiAccess::$user, $lineid, $datestart, $comment);
469
470
        if ($updateRes > 0) {
471
            $result = $this->get($id);
472
            unset($result->line);
473
            return $this->_cleanObjectDatas($result);
474
        }
475
476
        return false;
477
    }
478
479
    /**
480
     * Delete a line to given contract
481
     *
482
     *
483
     * @param int $id Id of contract to update
484
     * @param int $lineid Id of line to delete
485
     *
486
     * @url DELETE {id}/lines/{lineid}
487
     *
488
     * @return array|mixed
489
     *
490
     * @throws RestException 401
491
     * @throws RestException 404
492
     */
493
    public function deleteLine($id, $lineid)
494
    {
495
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
496
            throw new RestException(403);
497
        }
498
499
        $result = $this->contract->fetch($id);
500
        if (!$result) {
501
            throw new RestException(404, 'Contrat not found');
502
        }
503
504
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
505
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
506
        }
507
508
        // TODO Check the lineid $lineid is a line of object
509
510
        $updateRes = $this->contract->deleteLine($lineid, DolibarrApiAccess::$user);
511
        if ($updateRes > 0) {
512
            return $this->get($id);
513
        } else {
514
            throw new RestException(405, $this->contract->error);
515
        }
516
    }
517
518
    /**
519
     * Update contract general fields (won't touch lines of contract)
520
     *
521
     * @param int $id Id of contract to update
522
     * @param array $request_data Datas
523
     * @return  Object                      Updated object
524
     */
525
    public function put($id, $request_data = null)
526
    {
527
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
528
            throw new RestException(403);
529
        }
530
531
        $result = $this->contract->fetch($id);
532
        if (!$result) {
533
            throw new RestException(404, 'Contrat not found');
534
        }
535
536
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
537
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
538
        }
539
        foreach ($request_data as $field => $value) {
540
            if ($field == 'id') {
541
                continue;
542
            }
543
            if ($field === 'caller') {
544
                // 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
545
                $this->contract->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
546
                continue;
547
            }
548
            if ($field == 'array_options' && is_array($value)) {
549
                foreach ($value as $index => $val) {
550
                    $this->contract->array_options[$index] = $this->_checkValForAPI($field, $val, $this->contract);;
551
                }
552
                continue;
553
            }
554
555
            $this->contract->$field = $this->_checkValForAPI($field, $value, $this->contract);
556
        }
557
558
        if ($this->contract->update(DolibarrApiAccess::$user) > 0) {
559
            return $this->get($id);
560
        } else {
561
            throw new RestException(500, $this->contract->error);
562
        }
563
    }
564
565
    /**
566
     * Delete contract
567
     *
568
     * @param int $id Contract ID
569
     *
570
     * @return  array
571
     */
572
    public function delete($id)
573
    {
574
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'supprimer')) {
575
            throw new RestException(403);
576
        }
577
        $result = $this->contract->fetch($id);
578
        if (!$result) {
579
            throw new RestException(404, 'Contract not found');
580
        }
581
582
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
583
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
584
        }
585
586
        if (!$this->contract->delete(DolibarrApiAccess::$user)) {
587
            throw new RestException(500, 'Error when delete contract : ' . $this->contract->error);
588
        }
589
590
        return array(
591
            'success' => array(
592
                'code' => 200,
593
                'message' => 'Contract deleted'
594
            )
595
        );
596
    }
597
598
599
600
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
601
602
    /**
603
     * Validate a contract
604
     *
605
     * @param int $id Contract ID
606
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
607
     *
608
     * @url POST    {id}/validate
609
     *
610
     * @return  array
611
     * FIXME An error 403 is returned if the request has an empty body.
612
     * Error message: "Forbidden: Content type `text/plain` is not supported."
613
     * Workaround: send this in the body
614
     * {
615
     *   "notrigger": 0
616
     * }
617
     */
618
    public function validate($id, $notrigger = 0)
619
    {
620
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
621
            throw new RestException(403);
622
        }
623
        $result = $this->contract->fetch($id);
624
        if (!$result) {
625
            throw new RestException(404, 'Contract not found');
626
        }
627
628
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
629
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
630
        }
631
632
        $result = $this->contract->validate(DolibarrApiAccess::$user, '', $notrigger);
633
        if ($result == 0) {
634
            throw new RestException(304, 'Error nothing done. May be object is already validated');
635
        }
636
        if ($result < 0) {
637
            throw new RestException(500, 'Error when validating Contract: ' . $this->contract->error);
638
        }
639
640
        return array(
641
            'success' => array(
642
                'code' => 200,
643
                'message' => 'Contract validated (Ref=' . $this->contract->ref . ')'
644
            )
645
        );
646
    }
647
648
    /**
649
     * Close all services of a contract
650
     *
651
     * @param int $id Contract ID
652
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
653
     *
654
     * @url POST    {id}/close
655
     *
656
     * @return  array
657
     * FIXME An error 403 is returned if the request has an empty body.
658
     * Error message: "Forbidden: Content type `text/plain` is not supported."
659
     * Workaround: send this in the body
660
     * {
661
     *   "notrigger": 0
662
     * }
663
     */
664
    public function close($id, $notrigger = 0)
665
    {
666
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
667
            throw new RestException(403);
668
        }
669
        $result = $this->contract->fetch($id);
670
        if (!$result) {
671
            throw new RestException(404, 'Contract not found');
672
        }
673
674
        if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
675
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
676
        }
677
678
        $result = $this->contract->closeAll(DolibarrApiAccess::$user, $notrigger);
679
        if ($result == 0) {
680
            throw new RestException(304, 'Error nothing done. May be object is already close');
681
        }
682
        if ($result < 0) {
683
            throw new RestException(500, 'Error when closing Contract: ' . $this->contract->error);
684
        }
685
686
        return array(
687
            'success' => array(
688
                'code' => 200,
689
                'message' => 'Contract closed (Ref=' . $this->contract->ref . '). All services were closed.'
690
            )
691
        );
692
    }
693
}
694