BookKeeping::select_account()   B
last analyzed

Complexity

Conditions 7
Paths 10

Size

Total Lines 57
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 34
nc 10
nop 7
dl 0
loc 57
rs 8.4426
c 0
b 0
f 0

How to fix   Long Method   

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) 2014-2017  Olivier Geffroy             <[email protected]>
4
 * Copyright (C) 2015-2022  Alexandre Spangaro          <[email protected]>
5
 * Copyright (C) 2015-2020  Florian Henry               <[email protected]>
6
 * Copyright (C) 2018-2024  Frédéric France             <[email protected]>
7
 * Copyright (C) 2024		MDW							<[email protected]>
8
 * Copyright (C) 2024       Rafael San José             <[email protected]>
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
namespace Dolibarr\Code\Accountancy\Classes;
25
26
use Dolibarr\Code\Accountancy\Classes\AccountingAccount;
27
use Dolibarr\Code\Accountancy\Classes\AccountingJournal;
28
use Dolibarr\Code\Accountancy\Classes\BookKeepingLine;
29
use Dolibarr\Code\User\Classes\User;
30
use Dolibarr\Core\Base\CommonObject;
31
use Dolibarr\Core\Base\CommonObjectLine;
32
use DoliDB;
33
34
/**
35
 * \file        htdocs/accountancy/class/bookkeeping.class.php
36
 * \ingroup     Accountancy (Double entries)
37
 * \brief       File of class to manage Ledger (General Ledger and Subledger)
38
 */
39
40
/**
41
 * Class to manage Ledger (General Ledger and Subledger)
42
 */
43
class BookKeeping extends CommonObject
44
{
45
    /**
46
     * @var string Id to identify managed objects
47
     */
48
    public $element = 'accountingbookkeeping';
49
50
    /**
51
     * @var string Name of table without prefix where object is stored
52
     */
53
    public $table_element = 'accounting_bookkeeping';
54
55
    /**
56
     * @var int Entity
57
     */
58
    public $entity;
59
60
    /**
61
     * @var BookKeepingLine[] Lines
62
     */
63
    public $lines = array();
64
65
    /**
66
     * @var int ID
67
     */
68
    public $id;
69
70
    /**
71
     * @var int Date of source document, in db date NOT NULL
72
     */
73
    public $doc_date;
74
75
    /**
76
     * @var int Deadline for payment
77
     */
78
    public $date_lim_reglement;
79
80
    /**
81
     * @var string doc_type
82
     */
83
    public $doc_type;
84
85
    /**
86
     * @var string doc_ref
87
     */
88
    public $doc_ref;
89
90
    /**
91
     * @var int ID
92
     */
93
    public $fk_doc;
94
95
    /**
96
     * @var int ID
97
     */
98
    public $fk_docdet;
99
100
    /**
101
     * @var string thirdparty code
102
     */
103
    public $thirdparty_code;
104
105
    /**
106
     * @var string subledger account
107
     */
108
    public $subledger_account;
109
110
    /**
111
     * @var string subledger label
112
     */
113
    public $subledger_label;
114
115
    /**
116
     * @var string  doc_type
117
     */
118
    public $numero_compte;
119
120
    /**
121
     * @var string label compte
122
     */
123
    public $label_compte;
124
125
    /**
126
     * @var string label operation
127
     */
128
    public $label_operation;
129
130
    /**
131
     * @var float FEC:Debit
132
     */
133
    public $debit;
134
135
    /**
136
     * @var float FEC:Credit
137
     */
138
    public $credit;
139
140
    /**
141
     * @var float FEC:Amount (Not necessary)
142
     * @deprecated No more used (we have info into debit/credit and sens)
143
     */
144
    public $montant;
145
146
    /**
147
     * @var float FEC:Amount (Not necessary)
148
     * @deprecated No more used (we have info into debit/credit and sens)
149
     */
150
    public $amount;
151
152
    /**
153
     * @var string FEC:Sens (Not necessary)
154
     */
155
    public $sens;
156
157
    /**
158
     * @var int ID
159
     */
160
    public $fk_user_author;
161
162
    /**
163
     * @var string key for import
164
     */
165
    public $import_key;
166
167
    /**
168
     * @var string code journal
169
     */
170
    public $code_journal;
171
172
    /**
173
     * @var string label journal
174
     */
175
    public $journal_label;
176
177
    /**
178
     * @var int accounting transaction id
179
     */
180
    public $piece_num;
181
182
    /**
183
     * @var BookKeepingLine[] Movement line array
184
     */
185
    public $linesmvt = array();
186
187
    /**
188
     * @var BookKeepingLine[] export line array
189
     */
190
    public $linesexport = array();
191
192
    /**
193
     * @var integer|string date of movement validated & lock
194
     */
195
    public $date_validation;
196
197
    /**
198
     * @var integer|string date of movement who are noticed like exported
199
     */
200
    public $date_export;
201
202
    /**
203
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
204
     */
205
    public $picto = 'generic';
206
207
    /**
208
     * @var string[]    SQL filter used for check if the bookkeeping record can be created/inserted/modified/deleted (cached)
209
     */
210
    public static $can_modify_bookkeeping_sql_cached;
211
212
213
    /**
214
     * Constructor
215
     *
216
     * @param DoliDB $db Database handler
217
     */
218
    public function __construct(DoliDB $db)
219
    {
220
        $this->db = $db;
221
    }
222
223
    /**
224
     * Create object into database
225
     *
226
     * @param  User $user       User that creates
227
     * @param  int  $notrigger  false=launch triggers after, true=disable triggers
228
     * @return int              Return integer <0 if KO, Id of created object if OK
229
     */
230
    public function create(User $user, $notrigger = 0)
231
    {
232
        global $conf, $langs;
233
234
        dol_syslog(__METHOD__, LOG_DEBUG);
235
236
        $error = 0;
237
238
        // Clean parameters</center>
239
        if (isset($this->doc_type)) {
240
            $this->doc_type = trim($this->doc_type);
241
        }
242
        if (isset($this->doc_ref)) {
243
            $this->doc_ref = trim($this->doc_ref);
244
        }
245
        if (isset($this->fk_doc)) {
246
            $this->fk_doc = (int) $this->fk_doc;
247
        }
248
        if (isset($this->fk_docdet)) {
249
            $this->fk_docdet = (int) $this->fk_docdet;
250
        }
251
        if (isset($this->thirdparty_code)) {
252
            $this->thirdparty_code = trim($this->thirdparty_code);
253
        }
254
        if (isset($this->subledger_account)) {
255
            $this->subledger_account = trim($this->subledger_account);
256
        }
257
        if (isset($this->subledger_label)) {
258
            $this->subledger_label = trim($this->subledger_label);
259
        }
260
        if (isset($this->numero_compte)) {
261
            $this->numero_compte = trim($this->numero_compte);
262
        }
263
        if (isset($this->label_compte)) {
264
            $this->label_compte = trim($this->label_compte);
265
        }
266
        if (isset($this->label_operation)) {
267
            $this->label_operation = trim($this->label_operation);
268
        }
269
        if (isset($this->debit)) {
270
            $this->debit = (float) $this->debit;
271
        }
272
        if (isset($this->credit)) {
273
            $this->credit = (float) $this->credit;
274
        }
275
        if (isset($this->montant)) {
276
            $this->montant = (float) $this->montant;
277
        }
278
        if (isset($this->amount)) {
279
            $this->amount = (float) $this->amount;
280
        }
281
        if (isset($this->sens)) {
282
            $this->sens = trim($this->sens);
283
        }
284
        if (isset($this->import_key)) {
285
            $this->import_key = trim($this->import_key);
286
        }
287
        if (isset($this->code_journal)) {
288
            $this->code_journal = trim($this->code_journal);
289
        }
290
        if (isset($this->journal_label)) {
291
            $this->journal_label = trim($this->journal_label);
292
        }
293
        if (isset($this->piece_num)) {
294
            $this->piece_num = (int) $this->piece_num;
295
        }
296
        if (empty($this->debit)) {
297
            $this->debit = 0.0;
298
        }
299
        if (empty($this->credit)) {
300
            $this->credit = 0.0;
301
        }
302
303
        $result = $this->validBookkeepingDate($this->doc_date);
304
        if ($result < 0) {
305
            return -1;
306
        } elseif ($result == 0) {
307
            if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
308
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
309
            } else {
310
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
311
            }
312
            return -1;
313
        }
314
315
        // Check parameters
316
        if (($this->numero_compte == "") || $this->numero_compte == '-1' || $this->numero_compte == 'NotDefined') {
317
            $langs->loadLangs(array("errors"));
318
            if (in_array($this->doc_type, array('bank', 'expense_report'))) {
319
                $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet, $this->doc_type);
320
            } else {
321
                //$this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->doc_ref,  $this->label_compte);
322
                $mesg = $this->doc_ref . ', ' . $langs->trans("AccountAccounting") . ': ' . ($this->numero_compte != -1 ? $this->numero_compte : $langs->trans("Unknown"));
323
                if ($this->subledger_account && $this->subledger_account != $this->numero_compte) {
324
                    $mesg .= ', ' . $langs->trans("SubledgerAccount") . ': ' . $this->subledger_account;
325
                }
326
                $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForLine', $mesg);
327
            }
328
329
            return -1;
330
        }
331
332
        $this->db->begin();
333
334
        $this->piece_num = 0;
335
336
        // First check if line not yet already in bookkeeping.
337
        // Note that we must include 'doc_type - fk_doc - numero_compte - label' to be sure to have unicity of line (because we may have several lines
338
        // with same doc_type, fk_doc, numero_compte for 1 invoice line when using localtaxes with same account)
339
        // WARNING: This is not reliable, label may have been modified. This is just a small protection.
340
        // The page that make transfer make the test on couple (doc_type - fk_doc) only.
341
        $sql = "SELECT count(*) as nb";
342
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
343
        $sql .= " WHERE doc_type = '" . $this->db->escape($this->doc_type) . "'";
344
        $sql .= " AND fk_doc = " . ((int) $this->fk_doc);
345
        if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
346
            // DO NOT USE THIS IN PRODUCTION. This will generate a lot of trouble into reports and will corrupt database (by generating duplicate entries.
347
            $sql .= " AND fk_docdet = " . ((int) $this->fk_docdet); // This field can be 0 if record is for several lines
348
        }
349
        $sql .= " AND numero_compte = '" . $this->db->escape($this->numero_compte) . "'";
350
        $sql .= " AND label_operation = '" . $this->db->escape($this->label_operation) . "'";
351
        $sql .= " AND entity = " . $conf->entity; // Do not use getEntity for accounting features
352
353
        $resql = $this->db->query($sql);
354
355
        if ($resql) {
356
            $row = $this->db->fetch_object($resql);
357
            if ($row->nb == 0) {    // Not already into bookkeeping
358
                // Check to know if piece_num already exists for data we try to insert to reuse the same value
359
                $sqlnum = "SELECT piece_num";
360
                $sqlnum .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
361
                $sqlnum .= " WHERE doc_type = '" . $this->db->escape($this->doc_type) . "'"; // For example doc_type = 'bank'
362
                $sqlnum .= " AND fk_doc = " . ((int) $this->fk_doc);
363
                if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
364
                    // fk_docdet is rowid into llx_bank or llx_facturedet or llx_facturefourndet, or ...
365
                    $sqlnum .= " AND fk_docdet = " . ((int) $this->fk_docdet);
366
                }
367
                $sqlnum .= " AND doc_ref = '" . $this->db->escape($this->doc_ref) . "'"; // ref of source object
368
                $sqlnum .= " AND entity = " . $conf->entity; // Do not use getEntity for accounting features
369
370
                dol_syslog(get_only_class($this) . ":: create sqlnum=" . $sqlnum, LOG_DEBUG);
371
                $resqlnum = $this->db->query($sqlnum);
372
                if ($resqlnum) {
373
                    $objnum = $this->db->fetch_object($resqlnum);
374
                    $this->piece_num = $objnum->piece_num;
375
                }
376
377
                dol_syslog(get_only_class($this) . "::create this->piece_num=" . $this->piece_num, LOG_DEBUG);
378
                if (empty($this->piece_num)) {
379
                    $sqlnum = "SELECT MAX(piece_num)+1 as maxpiecenum";
380
                    $sqlnum .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
381
                    $sqlnum .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
382
383
                    $resqlnum = $this->db->query($sqlnum);
384
                    if ($resqlnum) {
385
                        $objnum = $this->db->fetch_object($resqlnum);
386
                        $this->piece_num = $objnum->maxpiecenum;
387
                    }
388
                    dol_syslog(get_only_class($this) . ":: create now this->piece_num=" . $this->piece_num, LOG_DEBUG);
389
                }
390
                if (empty($this->piece_num)) {
391
                    $this->piece_num = 1;
392
                }
393
394
                $now = dol_now();
395
396
                $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
397
                $sql .= "doc_date";
398
                $sql .= ", date_lim_reglement";
399
                $sql .= ", doc_type";
400
                $sql .= ", doc_ref";
401
                $sql .= ", fk_doc";
402
                $sql .= ", fk_docdet";
403
                $sql .= ", thirdparty_code";
404
                $sql .= ", subledger_account";
405
                $sql .= ", subledger_label";
406
                $sql .= ", numero_compte";
407
                $sql .= ", label_compte";
408
                $sql .= ", label_operation";
409
                $sql .= ", debit";
410
                $sql .= ", credit";
411
                $sql .= ", montant";
412
                $sql .= ", sens";
413
                $sql .= ", fk_user_author";
414
                $sql .= ", date_creation";
415
                $sql .= ", code_journal";
416
                $sql .= ", journal_label";
417
                $sql .= ", piece_num";
418
                $sql .= ', entity';
419
                $sql .= ") VALUES (";
420
                $sql .= "'" . $this->db->idate($this->doc_date) . "'";
421
                $sql .= ", " . (!isset($this->date_lim_reglement) || dol_strlen($this->date_lim_reglement) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_lim_reglement) . "'");
422
                $sql .= ", '" . $this->db->escape($this->doc_type) . "'";
423
                $sql .= ", '" . $this->db->escape($this->doc_ref) . "'";
424
                $sql .= ", " . ((int) $this->fk_doc);
425
                $sql .= ", " . ((int) $this->fk_docdet);
426
                $sql .= ", " . (!empty($this->thirdparty_code) ? ("'" . $this->db->escape($this->thirdparty_code) . "'") : "NULL");
427
                $sql .= ", " . (!empty($this->subledger_account) ? ("'" . $this->db->escape($this->subledger_account) . "'") : "NULL");
428
                $sql .= ", " . (!empty($this->subledger_label) ? ("'" . $this->db->escape($this->subledger_label) . "'") : "NULL");
429
                $sql .= ", '" . $this->db->escape($this->numero_compte) . "'";
430
                $sql .= ", " . (!empty($this->label_compte) ? ("'" . $this->db->escape($this->label_compte) . "'") : "NULL");
431
                $sql .= ", '" . $this->db->escape($this->label_operation) . "'";
432
                $sql .= ", " . ((float) $this->debit);
433
                $sql .= ", " . ((float) $this->credit);
434
                $sql .= ", " . ((float) $this->montant);
435
                $sql .= ", " . (!empty($this->sens) ? ("'" . $this->db->escape($this->sens) . "'") : "NULL");
436
                $sql .= ", '" . $this->db->escape($this->fk_user_author) . "'";
437
                $sql .= ", '" . $this->db->idate($now) . "'";
438
                $sql .= ", '" . $this->db->escape($this->code_journal) . "'";
439
                $sql .= ", " . (!empty($this->journal_label) ? ("'" . $this->db->escape($this->journal_label) . "'") : "NULL");
440
                $sql .= ", " . ((int) $this->piece_num);
441
                $sql .= ", " . (!isset($this->entity) ? $conf->entity : $this->entity);
442
                $sql .= ")";
443
444
                $resql = $this->db->query($sql);
445
                if ($resql) {
446
                    $id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
447
448
                    if ($id > 0) {
449
                        $this->id = $id;
450
                        $result = 0;
451
                    } else {
452
                        $result = -2;
453
                        $error++;
454
                        $this->errors[] = 'Error Create Error ' . $result . ' lecture ID';
455
                        dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
456
                    }
457
                } else {
458
                    $result = -1;
459
                    $error++;
460
                    $this->errors[] = 'Error ' . $this->db->lasterror();
461
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
462
                }
463
            } else {    // Already exists
464
                $result = -3;
465
                $error++;
466
                $this->error = 'BookkeepingRecordAlreadyExists';
467
                dol_syslog(__METHOD__ . ' ' . $this->error, LOG_WARNING);
468
            }
469
        } else {
470
            $result = -5;
471
            $error++;
472
            $this->errors[] = 'Error ' . $this->db->lasterror();
473
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
474
        }
475
476
        // Uncomment this and change MYOBJECT to your own tag if you
477
        // want this action to call a trigger.
478
        //if (! $error && ! $notrigger) {
479
480
        // // Call triggers
481
        // $result=$this->call_trigger('MYOBJECT_CREATE',$user);
482
        // if ($result < 0) $error++;
483
        // // End call triggers
484
        //}
485
486
        // Commit or rollback
487
        if ($error) {
488
            $this->db->rollback();
489
            return -1 * $error;
490
        } else {
491
            $this->db->commit();
492
            return $result;
493
        }
494
    }
495
496
    /**
497
     *  Return a link to the object card (with optionally the picto)
498
     *
499
     *  @param  int     $withpicto                  Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
500
     *  @param  string  $option                     On what the link point to ('nolink', ...)
501
     *  @param  int     $notooltip                  1=Disable tooltip
502
     *  @param  string  $morecss                    Add more css on link
503
     *  @param  int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
504
     *  @return string                              String with URL
505
     */
506
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
507
    {
508
        global $db, $conf, $langs;
509
        global $dolibarr_main_authentication, $dolibarr_main_demo;
510
        global $menumanager, $hookmanager;
511
512
        if (!empty($conf->dol_no_mouse_hover)) {
513
            $notooltip = 1; // Force disable tooltips
514
        }
515
516
        $result = '';
517
        $companylink = '';
518
519
        $label = '<u>' . $langs->trans("Transaction") . '</u>';
520
        $label .= '<br>';
521
        $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->piece_num;
522
523
        $url = constant('BASE_URL') . '/accountancy/bookkeeping/card.php?piece_num=' . $this->piece_num;
524
525
        if ($option != 'nolink') {
526
            // Add param to save lastsearch_values or not
527
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
528
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
529
                $add_save_lastsearch_values = 1;
530
            }
531
            if ($add_save_lastsearch_values) {
532
                $url .= '&save_lastsearch_values=1';
533
            }
534
        }
535
536
        $linkclose = '';
537
        if (empty($notooltip)) {
538
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
539
                $label = $langs->trans("ShowTransaction");
540
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
541
            }
542
            $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
543
            $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
544
        } else {
545
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
546
        }
547
548
        $linkstart = '<a href="' . $url . '"';
549
        $linkstart .= $linkclose . '>';
550
        $linkend = '</a>';
551
552
        $result .= $linkstart;
553
        if ($withpicto) {
554
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
555
        }
556
        if ($withpicto != 2) {
557
            $result .= $this->piece_num;
558
        }
559
        $result .= $linkend;
560
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
561
562
        global $action;
563
        $hookmanager->initHooks(array($this->element . 'dao'));
564
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
565
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
566
        if ($reshook > 0) {
567
            $result = $hookmanager->resPrint;
568
        } else {
569
            $result .= $hookmanager->resPrint;
570
        }
571
        return $result;
572
    }
573
574
    /**
575
     * Create object into database
576
     *
577
     * @param  User     $user       User that creates
578
     * @param  int      $notrigger  false=launch triggers after, true=disable triggers
579
     * @param  string   $mode       Mode
580
     * @return int                  Return integer <0 if KO, Id of created object if OK
581
     */
582
    public function createStd(User $user, $notrigger = 0, $mode = '')
583
    {
584
        global $conf, $langs;
585
586
        $langs->loadLangs(array("accountancy", "bills", "compta"));
587
588
        dol_syslog(__METHOD__, LOG_DEBUG);
589
590
        $error = 0;
591
592
        // Clean parameters
593
        if (isset($this->doc_type)) {
594
            $this->doc_type = trim($this->doc_type);
595
        }
596
        if (isset($this->doc_ref)) {
597
            $this->doc_ref = trim($this->doc_ref);
598
        }
599
        if (isset($this->fk_doc)) {
600
            $this->fk_doc = (int) $this->fk_doc;
601
        }
602
        if (isset($this->fk_docdet)) {
603
            $this->fk_docdet = (int) $this->fk_docdet;
604
        }
605
        if (isset($this->thirdparty_code)) {
606
            $this->thirdparty_code = trim($this->thirdparty_code);
607
        }
608
        if (isset($this->subledger_account)) {
609
            $this->subledger_account = trim($this->subledger_account);
610
        }
611
        if (isset($this->subledger_label)) {
612
            $this->subledger_label = trim($this->subledger_label);
613
        }
614
        if (isset($this->numero_compte)) {
615
            $this->numero_compte = trim($this->numero_compte);
616
        }
617
        if (isset($this->label_compte)) {
618
            $this->label_compte = trim($this->label_compte);
619
        }
620
        if (isset($this->label_operation)) {
621
            $this->label_operation = trim($this->label_operation);
622
        }
623
        if (isset($this->sens)) {
624
            $this->sens = trim($this->sens);
625
        }
626
        if (isset($this->import_key)) {
627
            $this->import_key = trim($this->import_key);
628
        }
629
        if (isset($this->code_journal)) {
630
            $this->code_journal = trim($this->code_journal);
631
        }
632
        if (isset($this->journal_label)) {
633
            $this->journal_label = trim($this->journal_label);
634
        }
635
        if (isset($this->piece_num)) {
636
            $this->piece_num = (int) $this->piece_num;
637
        }
638
        if (empty($this->debit)) {
639
            $this->debit = 0;
640
        }
641
        if (empty($this->credit)) {
642
            $this->credit = 0;
643
        }
644
        if (empty($this->montant)) {
645
            $this->montant = 0;
646
        }
647
648
        $result = $this->validBookkeepingDate($this->doc_date);
649
        if ($result < 0) {
650
            return -1;
651
        } elseif ($result == 0) {
652
            if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
653
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
654
            } else {
655
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
656
            }
657
            return -1;
658
        }
659
660
        $this->debit = (float) price2num($this->debit, 'MT');
661
        $this->credit = (float) price2num($this->credit, 'MT');
662
        $this->montant = (float) price2num($this->montant, 'MT');
663
664
        $now = dol_now();
665
666
        // Check parameters
667
        $this->journal_label = $langs->trans($this->journal_label);
668
669
        // Insert request
670
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . $mode . ' (';
671
        $sql .= 'doc_date,';
672
        $sql .= 'date_lim_reglement,';
673
        $sql .= 'doc_type,';
674
        $sql .= 'doc_ref,';
675
        $sql .= 'fk_doc,';
676
        $sql .= 'fk_docdet,';
677
        $sql .= 'thirdparty_code,';
678
        $sql .= 'subledger_account,';
679
        $sql .= 'subledger_label,';
680
        $sql .= 'numero_compte,';
681
        $sql .= 'label_compte,';
682
        $sql .= 'label_operation,';
683
        $sql .= 'debit,';
684
        $sql .= 'credit,';
685
        $sql .= 'montant,';
686
        $sql .= 'sens,';
687
        $sql .= 'fk_user_author,';
688
        $sql .= 'date_creation,';
689
        $sql .= 'code_journal,';
690
        $sql .= 'journal_label,';
691
        $sql .= 'piece_num,';
692
        $sql .= 'entity';
693
        $sql .= ') VALUES (';
694
        $sql .= ' ' . (!isset($this->doc_date) || dol_strlen($this->doc_date) == 0 ? 'NULL' : "'" . $this->db->idate($this->doc_date) . "'") . ',';
695
        $sql .= ' ' . (!isset($this->date_lim_reglement) || dol_strlen($this->date_lim_reglement) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_lim_reglement) . "'") . ',';
696
        $sql .= ' ' . (!isset($this->doc_type) ? 'NULL' : "'" . $this->db->escape($this->doc_type) . "'") . ',';
697
        $sql .= ' ' . (!isset($this->doc_ref) ? 'NULL' : "'" . $this->db->escape($this->doc_ref) . "'") . ',';
698
        $sql .= ' ' . (empty($this->fk_doc) ? '0' : (int) $this->fk_doc) . ',';
699
        $sql .= ' ' . (empty($this->fk_docdet) ? '0' : (int) $this->fk_docdet) . ',';
700
        $sql .= ' ' . (!isset($this->thirdparty_code) ? 'NULL' : "'" . $this->db->escape($this->thirdparty_code) . "'") . ',';
701
        $sql .= ' ' . (!isset($this->subledger_account) ? 'NULL' : "'" . $this->db->escape($this->subledger_account) . "'") . ',';
702
        $sql .= ' ' . (!isset($this->subledger_label) ? 'NULL' : "'" . $this->db->escape($this->subledger_label) . "'") . ',';
703
        $sql .= ' ' . (!isset($this->numero_compte) ? 'NULL' : "'" . $this->db->escape($this->numero_compte) . "'") . ',';
704
        $sql .= ' ' . (!isset($this->label_compte) ? 'NULL' : "'" . $this->db->escape($this->label_compte) . "'") . ',';
705
        $sql .= ' ' . (!isset($this->label_operation) ? 'NULL' : "'" . $this->db->escape($this->label_operation) . "'") . ',';
706
        $sql .= ' ' . (!isset($this->debit) ? 'NULL' : $this->debit) . ',';
707
        $sql .= ' ' . (!isset($this->credit) ? 'NULL' : $this->credit) . ',';
708
        $sql .= ' ' . (!isset($this->montant) ? 'NULL' : $this->montant) . ',';
709
        $sql .= ' ' . (!isset($this->sens) ? 'NULL' : "'" . $this->db->escape($this->sens) . "'") . ',';
710
        $sql .= ' ' . ((int) $user->id) . ',';
711
        $sql .= ' ' . "'" . $this->db->idate($now) . "',";
712
        $sql .= ' ' . (empty($this->code_journal) ? 'NULL' : "'" . $this->db->escape($this->code_journal) . "'") . ',';
713
        $sql .= ' ' . (empty($this->journal_label) ? 'NULL' : "'" . $this->db->escape($this->journal_label) . "'") . ',';
714
        $sql .= ' ' . (empty($this->piece_num) ? 'NULL' : $this->db->escape($this->piece_num)) . ',';
715
        $sql .= ' ' . (!isset($this->entity) ? $conf->entity : $this->entity);
716
        $sql .= ')';
717
718
        $this->db->begin();
719
720
        $resql = $this->db->query($sql);
721
        if (!$resql) {
722
            $error++;
723
            $this->errors[] = 'Error ' . $this->db->lasterror();
724
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
725
        }
726
727
        if (!$error) {
728
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element . $mode);
729
        }
730
731
        // Commit or rollback
732
        if ($error) {
733
            $this->db->rollback();
734
735
            return -1 * $error;
736
        } else {
737
            $this->db->commit();
738
739
            return $this->id;
740
        }
741
    }
742
743
    /**
744
     * Load object in memory from the database
745
     *
746
     * @param int           $id     Id object
747
     * @param string|null   $ref    Ref
748
     * @param string        $mode   Mode ('' or 'tmp_')
749
     * @return int                  Return integer <0 if KO, 0 if not found, >0 if OK
750
     */
751
    public function fetch($id, $ref = null, $mode = '')
752
    {
753
        global $conf;
754
755
        dol_syslog(__METHOD__, LOG_DEBUG);
756
757
        $sql = 'SELECT';
758
        $sql .= ' t.rowid,';
759
        $sql .= " t.doc_date,";
760
        $sql .= " t.date_lim_reglement,";
761
        $sql .= " t.doc_type,";
762
        $sql .= " t.doc_ref,";
763
        $sql .= " t.fk_doc,";
764
        $sql .= " t.fk_docdet,";
765
        $sql .= " t.thirdparty_code,";
766
        $sql .= " t.subledger_account,";
767
        $sql .= " t.subledger_label,";
768
        $sql .= " t.numero_compte,";
769
        $sql .= " t.label_compte,";
770
        $sql .= " t.label_operation,";
771
        $sql .= " t.debit,";
772
        $sql .= " t.credit,";
773
        $sql .= " t.montant as amount,";
774
        $sql .= " t.sens,";
775
        $sql .= " t.fk_user_author,";
776
        $sql .= " t.import_key,";
777
        $sql .= " t.code_journal,";
778
        $sql .= " t.journal_label,";
779
        $sql .= " t.piece_num,";
780
        $sql .= " t.date_creation,";
781
        // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
782
        if ($mode != "_tmp") {
783
            $sql .= " t.date_export,";
784
        }
785
        $sql .= " t.date_validated as date_validation";
786
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . $mode . ' as t';
787
        $sql .= ' WHERE 1 = 1';
788
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
789
        if (null !== $ref) {
790
            $sql .= " AND t.rowid = " . ((int) $ref);
791
        } else {
792
            $sql .= " AND t.rowid = " . ((int) $id);
793
        }
794
795
        $resql = $this->db->query($sql);
796
        if ($resql) {
797
            $numrows = $this->db->num_rows($resql);
798
            if ($numrows) {
799
                $obj = $this->db->fetch_object($resql);
800
801
                $this->id = $obj->rowid;
802
803
                $this->doc_date = $this->db->jdate($obj->doc_date);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->doc_date) can also be of type string. However, the property $doc_date is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
804
                $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_lim_reglement) can also be of type string. However, the property $date_lim_reglement is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
805
                $this->doc_type = $obj->doc_type;
806
                $this->doc_ref = $obj->doc_ref;
807
                $this->fk_doc = $obj->fk_doc;
808
                $this->fk_docdet = $obj->fk_docdet;
809
                $this->thirdparty_code = $obj->thirdparty_code;
810
                $this->subledger_account = $obj->subledger_account;
811
                $this->subledger_label = $obj->subledger_label;
812
                $this->numero_compte = $obj->numero_compte;
813
                $this->label_compte = $obj->label_compte;
814
                $this->label_operation = $obj->label_operation;
815
                $this->debit = $obj->debit;
816
                $this->credit = $obj->credit;
817
                $this->montant = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...s\BookKeeping::$montant has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

817
                /** @scrutinizer ignore-deprecated */ $this->montant = $obj->amount;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
818
                $this->amount = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...es\BookKeeping::$amount has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

818
                /** @scrutinizer ignore-deprecated */ $this->amount = $obj->amount;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
819
                $this->sens = $obj->sens;
820
                $this->fk_user_author = $obj->fk_user_author;
821
                $this->import_key = $obj->import_key;
822
                $this->code_journal = $obj->code_journal;
823
                $this->journal_label = $obj->journal_label;
824
                $this->piece_num = $obj->piece_num;
825
                $this->date_creation = $this->db->jdate($obj->date_creation);
826
                $this->date_export = $this->db->jdate($obj->date_export);
827
                $this->date_validation = isset($obj->date_validation) ? $this->db->jdate($obj->date_validation) : '';
828
            }
829
            $this->db->free($resql);
830
831
            if ($numrows) {
832
                return 1;
833
            } else {
834
                return 0;
835
            }
836
        } else {
837
            $this->errors[] = 'Error ' . $this->db->lasterror();
838
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
839
840
            return -1;
841
        }
842
    }
843
844
845
    /**
846
     * Load object in memory from the database in ->lines. Or just make a simple count if $countonly=1.
847
     *
848
     * @param   string  $sortorder      Sort Order
849
     * @param   string  $sortfield      Sort field
850
     * @param   int     $limit          limit
851
     * @param   int     $offset         offset limit
852
     * @param   array   $filter         filter array
853
     * @param   string  $filtermode     filter mode (AND or OR)
854
     * @param   int     $option         option (0: general account or 1: subaccount)
855
     * @param   int     $countonly      Do not fill the $object->lines, return only the count.
856
     * @return  int                     Return integer <0 if KO, Number of lines if OK
857
     */
858
    public function fetchAllByAccount($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0, $countonly = 0)
859
    {
860
        global $conf;
861
862
        dol_syslog(__METHOD__, LOG_DEBUG);
863
864
        $this->lines = array();
865
        $num = 0;
866
867
        $sql = 'SELECT';
868
        if ($countonly) {
869
            $sql .= ' COUNT(t.rowid) as nb';
870
        } else {
871
            $sql .= ' t.rowid,';
872
            $sql .= " t.doc_date,";
873
            $sql .= " t.doc_type,";
874
            $sql .= " t.doc_ref,";
875
            $sql .= " t.fk_doc,";
876
            $sql .= " t.fk_docdet,";
877
            $sql .= " t.thirdparty_code,";
878
            $sql .= " t.subledger_account,";
879
            $sql .= " t.subledger_label,";
880
            $sql .= " t.numero_compte,";
881
            $sql .= " t.label_compte,";
882
            $sql .= " t.label_operation,";
883
            $sql .= " t.debit,";
884
            $sql .= " t.credit,";
885
            $sql .= " t.montant as amount,";
886
            $sql .= " t.sens,";
887
            $sql .= " t.multicurrency_amount,";
888
            $sql .= " t.multicurrency_code,";
889
            $sql .= " t.lettering_code,";
890
            $sql .= " t.date_lettering,";
891
            $sql .= " t.fk_user_author,";
892
            $sql .= " t.import_key,";
893
            $sql .= " t.code_journal,";
894
            $sql .= " t.journal_label,";
895
            $sql .= " t.piece_num,";
896
            $sql .= " t.date_creation,";
897
            $sql .= " t.date_export,";
898
            $sql .= " t.date_validated as date_validation,";
899
            $sql .= " t.date_lim_reglement,";
900
            $sql .= " t.import_key";
901
        }
902
        // Manage filter
903
        $sqlwhere = array();
904
        if (count($filter) > 0) {
905
            foreach ($filter as $key => $value) {
906
                if ($key == 't.doc_date>=') {
907
                    $sqlwhere[] = "t.doc_date >= '" . $this->db->idate($value) . "'";
908
                } elseif ($key == 't.doc_date<=') {
909
                    $sqlwhere[] = "t.doc_date <= '" . $this->db->idate($value) . "'";
910
                } elseif ($key == 't.doc_date>') {
911
                    $sqlwhere[] = "t.doc_date > '" . $this->db->idate($value) . "'";
912
                } elseif ($key == 't.doc_date<') {
913
                    $sqlwhere[] = "t.doc_date < '" . $this->db->idate($value) . "'";
914
                } elseif ($key == 't.numero_compte>=') {
915
                    $sqlwhere[] = "t.numero_compte >= '" . $this->db->escape($value) . "'";
916
                } elseif ($key == 't.numero_compte<=') {
917
                    $sqlwhere[] = "t.numero_compte <= '" . $this->db->escape($value) . "'";
918
                } elseif ($key == 't.subledger_account>=') {
919
                    $sqlwhere[] = "t.subledger_account >= '" . $this->db->escape($value) . "'";
920
                } elseif ($key == 't.subledger_account<=') {
921
                    $sqlwhere[] = "t.subledger_account <= '" . $this->db->escape($value) . "'";
922
                } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
923
                    $sqlwhere[] = $this->db->sanitize($key) . ' = ' . ((int) $value);
924
                } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
925
                    $sqlwhere[] = $this->db->sanitize($key) . ' LIKE \'' . $this->db->escape($this->db->escapeforlike($value)) . '%\'';
926
                } elseif ($key == 't.date_creation>=') {
927
                    $sqlwhere[] = 't.date_creation >= \'' . $this->db->idate($value) . '\'';
928
                } elseif ($key == 't.date_creation<=') {
929
                    $sqlwhere[] = 't.date_creation <= \'' . $this->db->idate($value) . '\'';
930
                } elseif ($key == 't.date_export>=') {
931
                    $sqlwhere[] = 't.date_export >= \'' . $this->db->idate($value) . '\'';
932
                } elseif ($key == 't.date_export<=') {
933
                    $sqlwhere[] = 't.date_export <= \'' . $this->db->idate($value) . '\'';
934
                } elseif ($key == 't.date_validated>=') {
935
                    $sqlwhere[] = 't.date_validated >= \'' . $this->db->idate($value) . '\'';
936
                } elseif ($key == 't.date_validated<=') {
937
                    $sqlwhere[] = 't.date_validated <= \'' . $this->db->idate($value) . '\'';
938
                } elseif ($key == 't.date_lim_reglement>=') {
939
                    $sqlwhere[] = 't.date_lim_reglement>=\'' . $this->db->idate($value) . '\'';
940
                } elseif ($key == 't.date_lim_reglement<=') {
941
                    $sqlwhere[] = 't.date_lim_reglement<=\'' . $this->db->idate($value) . '\'';
942
                } elseif ($key == 't.credit' || $key == 't.debit') {
943
                    $sqlwhere[] = natural_search($key, $value, 1, 1);
944
                } elseif ($key == 't.reconciled_option') {
945
                    $sqlwhere[] = 't.lettering_code IS NULL';
946
                } elseif ($key == 't.code_journal' && !empty($value)) {
947
                    if (is_array($value)) {
948
                        $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
949
                    } else {
950
                        $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
951
                    }
952
                } elseif ($key == 't.search_accounting_code_in' && !empty($value)) {
953
                    $sqlwhere[] = 't.numero_compte IN (' . $this->db->sanitize($value, 1) . ')';
954
                } else {
955
                    $sqlwhere[] = natural_search($key, $value, 0, 1);
956
                }
957
            }
958
        }
959
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
960
        $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
961
        if (count($sqlwhere) > 0) {
962
            $sql .= " AND " . implode(" " . $this->db->sanitize($filtermode) . " ", $sqlwhere);
963
        }
964
        // Filter by ledger account or subledger account
965
        if (!empty($option)) {
966
            $sql .= " AND t.subledger_account IS NOT NULL";
967
            $sql .= " AND t.subledger_account <> ''";
968
            $sortfield = 't.subledger_account' . ($sortfield ? ',' . $sortfield : '');
969
            $sortorder = 'ASC' . ($sortfield ? ',' . $sortfield : '');
970
        } else {
971
            $sortfield = 't.numero_compte' . ($sortfield ? ',' . $sortfield : '');
972
            $sortorder = 'ASC' . ($sortorder ? ',' . $sortorder : '');
973
        }
974
975
        if (!$countonly) {
976
            $sql .= $this->db->order($sortfield, $sortorder);
977
            if (!empty($limit)) {
978
                $sql .= $this->db->plimit($limit + 1, $offset);
979
            }
980
        }
981
982
        $resql = $this->db->query($sql);
983
        if ($resql) {
984
            if ($countonly) {
985
                $obj = $this->db->fetch_object($resql);
986
                if ($obj) {
987
                    $num = $obj->nb;
988
                }
989
            } else {
990
                $num = $this->db->num_rows($resql);
991
992
                $i = 0;
993
                while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
994
                    $line = new BookKeepingLine($this->db);
995
996
                    $line->id = $obj->rowid;
997
998
                    $line->doc_date = $this->db->jdate($obj->doc_date);
999
                    $line->doc_type = $obj->doc_type;
1000
                    $line->doc_ref = $obj->doc_ref;
1001
                    $line->fk_doc = $obj->fk_doc;
1002
                    $line->fk_docdet = $obj->fk_docdet;
1003
                    $line->thirdparty_code = $obj->thirdparty_code;
1004
                    $line->subledger_account = $obj->subledger_account;
1005
                    $line->subledger_label = $obj->subledger_label;
1006
                    $line->numero_compte = $obj->numero_compte;
1007
                    $line->label_compte = $obj->label_compte;
1008
                    $line->label_operation = $obj->label_operation;
1009
                    $line->debit = $obj->debit;
1010
                    $line->credit = $obj->credit;
1011
                    $line->montant = $obj->amount; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...okKeepingLine::$montant has been deprecated: see $amount ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1011
                    /** @scrutinizer ignore-deprecated */ $line->montant = $obj->amount; // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1012
                    $line->amount = $obj->amount;
1013
                    $line->sens = $obj->sens;
1014
                    $line->multicurrency_amount = $obj->multicurrency_amount;
1015
                    $line->multicurrency_code = $obj->multicurrency_code;
1016
                    $line->lettering_code = $obj->lettering_code;
1017
                    $line->date_lettering = $obj->date_lettering;
1018
                    $line->fk_user_author = $obj->fk_user_author;
1019
                    $line->import_key = $obj->import_key;
1020
                    $line->code_journal = $obj->code_journal;
1021
                    $line->journal_label = $obj->journal_label;
1022
                    $line->piece_num = $obj->piece_num;
1023
                    $line->date_creation = $this->db->jdate($obj->date_creation);
1024
                    $line->date_export = $this->db->jdate($obj->date_export);
1025
                    $line->date_validation = $this->db->jdate($obj->date_validation);
1026
                    // Due date
1027
                    $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1028
                    $line->import_key = $obj->import_key;
1029
1030
                    $this->lines[] = $line;
1031
1032
                    $i++;
1033
                }
1034
            }
1035
            $this->db->free($resql);
1036
1037
            return $num;
1038
        } else {
1039
            $this->errors[] = 'Error ' . $this->db->lasterror();
1040
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1041
1042
            return -1;
1043
        }
1044
    }
1045
1046
    /**
1047
     * Load object in memory from the database
1048
     *
1049
     * @param string        $sortorder                      Sort Order
1050
     * @param string        $sortfield                      Sort field
1051
     * @param int           $limit                          Limit
1052
     * @param int           $offset                         Offset limit
1053
     * @param string|array  $filter                         Filter array
1054
     * @param string        $filtermode                     Filter mode (AND or OR)
1055
     * @param int           $showAlreadyExportMovements     Show movements when field 'date_export' is not empty (0:No / 1:Yes (Default))
1056
     * @return int                                          Return integer <0 if KO, >0 if OK
1057
     */
1058
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $showAlreadyExportMovements = 1)
1059
    {
1060
        global $conf;
1061
1062
        dol_syslog(__METHOD__, LOG_DEBUG);
1063
1064
        $sql = 'SELECT';
1065
        $sql .= ' t.rowid,';
1066
        $sql .= " t.doc_date,";
1067
        $sql .= " t.doc_type,";
1068
        $sql .= " t.doc_ref,";
1069
        $sql .= " t.fk_doc,";
1070
        $sql .= " t.fk_docdet,";
1071
        $sql .= " t.thirdparty_code,";
1072
        $sql .= " t.subledger_account,";
1073
        $sql .= " t.subledger_label,";
1074
        $sql .= " t.numero_compte,";
1075
        $sql .= " t.label_compte,";
1076
        $sql .= " t.label_operation,";
1077
        $sql .= " t.debit,";
1078
        $sql .= " t.credit,";
1079
        $sql .= " t.lettering_code,";
1080
        $sql .= " t.date_lettering,";
1081
        $sql .= " t.montant as amount,";
1082
        $sql .= " t.sens,";
1083
        $sql .= " t.fk_user_author,";
1084
        $sql .= " t.import_key,";
1085
        $sql .= " t.code_journal,";
1086
        $sql .= " t.journal_label,";
1087
        $sql .= " t.piece_num,";
1088
        $sql .= " t.date_creation,";
1089
        $sql .= " t.date_lim_reglement,";
1090
        $sql .= " t.tms as date_modification,";
1091
        $sql .= " t.date_export,";
1092
        $sql .= " t.date_validated as date_validation";
1093
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
1094
1095
        $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1096
        if ($showAlreadyExportMovements == 0) {
1097
            $sql .= " AND t.date_export IS NULL";
1098
        }
1099
1100
        // Manage filter
1101
        if (is_array($filter)) {    // deprecated, use $filter = USF syntax
1102
            $sqlwhere = array();
1103
            if (count($filter) > 0) {
1104
                foreach ($filter as $key => $value) {
1105
                    if ($key == 't.doc_date') {
1106
                        $sqlwhere[] = $this->db->sanitize($key) . ' = \'' . $this->db->idate($value) . '\'';
1107
                    } elseif ($key == 't.doc_date>=') {
1108
                        $sqlwhere[] = "t.doc_date >= '" . $this->db->idate($value) . "'";
1109
                    } elseif ($key == 't.doc_date<=') {
1110
                        $sqlwhere[] = "t.doc_date <= '" . $this->db->idate($value) . "'";
1111
                    } elseif ($key == 't.doc_date>') {
1112
                        $sqlwhere[] = "t.doc_date > '" . $this->db->idate($value) . "'";
1113
                    } elseif ($key == 't.doc_date<') {
1114
                        $sqlwhere[] = "t.doc_date < '" . $this->db->idate($value) . "'";
1115
                    } elseif ($key == 't.numero_compte>=') {
1116
                        $sqlwhere[] = "t.numero_compte >= '" . $this->db->escape($value) . "'";
1117
                    } elseif ($key == 't.numero_compte<=') {
1118
                        $sqlwhere[] = "t.numero_compte <= '" . $this->db->escape($value) . "'";
1119
                    } elseif ($key == 't.subledger_account>=') {
1120
                        $sqlwhere[] = "t.subledger_account >= '" . $this->db->escape($value) . "'";
1121
                    } elseif ($key == 't.subledger_account<=') {
1122
                        $sqlwhere[] = "t.subledger_account <= '" . $this->db->escape($value) . "'";
1123
                    } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1124
                        $sqlwhere[] = $this->db->sanitize($key) . ' = ' . ((int) $value);
1125
                    } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1126
                        $sqlwhere[] = $this->db->sanitize($key) . ' LIKE \'' . $this->db->escape($value) . '%\'';
1127
                    } elseif ($key == 't.date_creation>=') {
1128
                        $sqlwhere[] = 't.date_creation >= \'' . $this->db->idate($value) . '\'';
1129
                    } elseif ($key == 't.date_creation<=') {
1130
                        $sqlwhere[] = 't.date_creation <= \'' . $this->db->idate($value) . '\'';
1131
                    } elseif ($key == 't.tms>=') {
1132
                        $sqlwhere[] = 't.tms >= \'' . $this->db->idate($value) . '\'';
1133
                    } elseif ($key == 't.tms<=') {
1134
                        $sqlwhere[] = 't.tms <= \'' . $this->db->idate($value) . '\'';
1135
                    } elseif ($key == 't.date_export>=') {
1136
                        $sqlwhere[] = 't.date_export >= \'' . $this->db->idate($value) . '\'';
1137
                    } elseif ($key == 't.date_export<=') {
1138
                        $sqlwhere[] = 't.date_export <= \'' . $this->db->idate($value) . '\'';
1139
                    } elseif ($key == 't.date_validated>=') {
1140
                        $sqlwhere[] = 't.date_validated >= \'' . $this->db->idate($value) . '\'';
1141
                    } elseif ($key == 't.date_validated<=') {
1142
                        $sqlwhere[] = 't.date_validated <= \'' . $this->db->idate($value) . '\'';
1143
                    } elseif ($key == 't.credit' || $key == 't.debit') {
1144
                        $sqlwhere[] = natural_search($key, $value, 1, 1);
1145
                    } elseif ($key == 't.code_journal' && !empty($value)) {
1146
                        if (is_array($value)) {
1147
                            $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1148
                        } else {
1149
                            $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1150
                        }
1151
                    } else {
1152
                        $sqlwhere[] = natural_search($key, $value, 0, 1);
1153
                    }
1154
                }
1155
            }
1156
            if (count($sqlwhere) > 0) {
1157
                $sql .= ' AND ' . implode(" " . $this->db->sanitize($filtermode) . " ", $sqlwhere);
1158
            }
1159
1160
            $filter = '';
1161
        }
1162
1163
        // Manage filter
1164
        $errormessage = '';
1165
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1166
        if ($errormessage) {
1167
            $this->errors[] = $errormessage;
1168
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1169
            return -1;
1170
        }
1171
1172
        if (!empty($sortfield)) {
1173
            $sql .= $this->db->order($sortfield, $sortorder);
1174
        }
1175
        if (!empty($limit)) {
1176
            $sql .= $this->db->plimit($limit + 1, $offset);
1177
        }
1178
        $this->lines = array();
1179
1180
        $resql = $this->db->query($sql);
1181
        if ($resql) {
1182
            $num = $this->db->num_rows($resql);
1183
1184
            $i = 0;
1185
            while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1186
                $line = new BookKeepingLine($this->db);
1187
1188
                $line->id = $obj->rowid;
1189
1190
                $line->doc_date = $this->db->jdate($obj->doc_date);
1191
                $line->doc_type = $obj->doc_type;
1192
                $line->doc_ref = $obj->doc_ref;
1193
                $line->fk_doc = $obj->fk_doc;
1194
                $line->fk_docdet = $obj->fk_docdet;
1195
                $line->thirdparty_code = $obj->thirdparty_code;
1196
                $line->subledger_account = $obj->subledger_account;
1197
                $line->subledger_label = $obj->subledger_label;
1198
                $line->numero_compte = $obj->numero_compte;
1199
                $line->label_compte = $obj->label_compte;
1200
                $line->label_operation = $obj->label_operation;
1201
                $line->debit = $obj->debit;
1202
                $line->credit = $obj->credit;
1203
                $line->montant = $obj->amount; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...okKeepingLine::$montant has been deprecated: see $amount ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1203
                /** @scrutinizer ignore-deprecated */ $line->montant = $obj->amount; // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1204
                $line->amount = $obj->amount;
1205
                $line->sens = $obj->sens;
1206
                $line->lettering_code = $obj->lettering_code;
1207
                $line->date_lettering = $obj->date_lettering;
1208
                $line->fk_user_author = $obj->fk_user_author;
1209
                $line->import_key = $obj->import_key;
1210
                $line->code_journal = $obj->code_journal;
1211
                $line->journal_label = $obj->journal_label;
1212
                $line->piece_num = $obj->piece_num;
1213
                $line->date_creation = $this->db->jdate($obj->date_creation);
1214
                $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1215
                $line->date_modification = $this->db->jdate($obj->date_modification);
1216
                $line->date_export = $this->db->jdate($obj->date_export);
1217
                $line->date_validation = $this->db->jdate($obj->date_validation);
1218
1219
                $this->lines[] = $line;
1220
1221
                $i++;
1222
            }
1223
            $this->db->free($resql);
1224
1225
            return $num;
1226
        } else {
1227
            $this->errors[] = 'Error ' . $this->db->lasterror();
1228
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1229
            return -1;
1230
        }
1231
    }
1232
1233
    /**
1234
     * Load object in memory from the database
1235
     *
1236
     * @param   string          $sortorder      Sort Order
1237
     * @param   string          $sortfield      Sort field
1238
     * @param   int             $limit          Limit
1239
     * @param   int             $offset         Offset limit
1240
     * @param   string|array    $filter         Filter
1241
     * @param   string          $filtermode     Filter mode (AND or OR)
1242
     * @param   int             $option         option (0: aggregate by general account or 1: aggreegate by subaccount)
1243
     * @return  int                             Return integer <0 if KO, >0 if OK
1244
     */
1245
    public function fetchAllBalance($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $option = 0)
1246
    {
1247
        global $conf;
1248
1249
        $this->lines = array();
1250
1251
        dol_syslog(__METHOD__, LOG_DEBUG);
1252
1253
        $sql = 'SELECT';
1254
        $sql .= " t.numero_compte,";
1255
        if (!empty($option)) {
1256
            $sql .= " t.subledger_account,";
1257
            $sql .= " t.subledger_label,";
1258
        }
1259
        $sql .= " SUM(t.debit) as debit,";
1260
        $sql .= " SUM(t.credit) as credit";
1261
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
1262
        $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1263
1264
        // Manage filter
1265
        if (is_array($filter)) {
1266
            $sqlwhere = array();
1267
            if (count($filter) > 0) {
1268
                foreach ($filter as $key => $value) {
1269
                    if ($key == 't.doc_date') {
1270
                        $sqlwhere[] = $this->db->sanitize($key) . " = '" . $this->db->idate($value) . "'";
1271
                    } elseif ($key == 't.doc_date>=') {
1272
                        $sqlwhere[] = "t.doc_date >= '" . $this->db->idate($value) . "'";
1273
                    } elseif ($key == 't.doc_date<=') {
1274
                        $sqlwhere[] = "t.doc_date <= '" . $this->db->idate($value) . "'";
1275
                    } elseif ($key == 't.doc_date>') {
1276
                        $sqlwhere[] = "t.doc_date > '" . $this->db->idate($value) . "'";
1277
                    } elseif ($key == 't.doc_date<') {
1278
                        $sqlwhere[] = "t.doc_date < '" . $this->db->idate($value) . "'";
1279
                    } elseif ($key == 't.numero_compte>=') {
1280
                        $sqlwhere[] = "t.numero_compte >= '" . $this->db->escape($value) . "'";
1281
                    } elseif ($key == 't.numero_compte<=') {
1282
                        $sqlwhere[] = "t.numero_compte <= '" . $this->db->escape($value) . "'";
1283
                    } elseif ($key == 't.subledger_account>=') {
1284
                        $sqlwhere[] = "t.subledger_account >= '" . $this->db->escape($value) . "'";
1285
                    } elseif ($key == 't.subledger_account<=') {
1286
                        $sqlwhere[] = "t.subledger_account <= '" . $this->db->escape($value) . "'";
1287
                    } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1288
                        $sqlwhere[] = $this->db->sanitize($key) . " = " . ((int) $value);
1289
                    } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1290
                        $sqlwhere[] = $this->db->sanitize($key) . " LIKE '" . $this->db->escape($value) . "%'";
1291
                    } elseif ($key == 't.subledger_label') {
1292
                        $sqlwhere[] = $this->db->sanitize($key) . " LIKE '" . $this->db->escape($value) . "%'";
1293
                    } elseif ($key == 't.code_journal' && !empty($value)) {
1294
                        if (is_array($value)) {
1295
                            $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1296
                        } else {
1297
                            $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1298
                        }
1299
                    } elseif ($key == 't.reconciled_option') {
1300
                        $sqlwhere[] = 't.lettering_code IS NULL';
1301
                    } else {
1302
                        $sqlwhere[] = $this->db->sanitize($key) . " LIKE '%" . $this->db->escape($this->db->escapeforlike($value)) . "%'";
1303
                    }
1304
                }
1305
            }
1306
            if (count($sqlwhere) > 0) {
1307
                $sql .= " AND " . implode(" " . $this->db->sanitize($filtermode) . " ", $sqlwhere);
1308
            }
1309
1310
            $filter = '';
1311
        }
1312
1313
        // Manage filter
1314
        $errormessage = '';
1315
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1316
        if ($errormessage) {
1317
            $this->errors[] = $errormessage;
1318
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1319
            return -1;
1320
        }
1321
1322
        if (!empty($option)) {
1323
            $sql .= " AND t.subledger_account IS NOT NULL";
1324
            $sql .= " AND t.subledger_account <> ''";
1325
            $sql .= " GROUP BY t.numero_compte, t.subledger_account, t.subledger_label";
1326
            $sortfield = 't.subledger_account' . ($sortfield ? ',' . $sortfield : '');
1327
            $sortorder = 'ASC' . ($sortfield ? ',' . $sortfield : '');
1328
        } else {
1329
            $sql .= ' GROUP BY t.numero_compte';
1330
            $sortfield = 't.numero_compte' . ($sortfield ? ',' . $sortfield : '');
1331
            $sortorder = 'ASC' . ($sortorder ? ',' . $sortorder : '');
1332
        }
1333
1334
        if (!empty($sortfield)) {
1335
            $sql .= $this->db->order($sortfield, $sortorder);
1336
        }
1337
        if (!empty($limit)) {
1338
            $sql .= $this->db->plimit($limit + 1, $offset);
1339
        }
1340
1341
        //print $sql;
1342
        $resql = $this->db->query($sql);
1343
1344
        if ($resql) {
1345
            $num = $this->db->num_rows($resql);
1346
1347
            $i = 0;
1348
            while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1349
                $line = new BookKeepingLine($this->db);
1350
1351
                $line->numero_compte = $obj->numero_compte;
1352
                //$line->label_compte = $obj->label_compte;
1353
                if (!empty($option)) {
1354
                    $line->subledger_account = $obj->subledger_account;
1355
                    $line->subledger_label = $obj->subledger_label;
1356
                }
1357
                $line->debit = $obj->debit;
1358
                $line->credit = $obj->credit;
1359
1360
                $this->lines[] = $line;
1361
1362
                $i++;
1363
            }
1364
            $this->db->free($resql);
1365
1366
            return $num;
1367
        } else {
1368
            $this->errors[] = 'Error ' . $this->db->lasterror();
1369
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1370
1371
            return -1;
1372
        }
1373
    }
1374
1375
    /**
1376
     * Update object into database
1377
     *
1378
     * @param  User     $user       User that modifies
1379
     * @param  int      $notrigger  false=launch triggers after, true=disable triggers
1380
     * @param  string   $mode       Mode ('' or _tmp')
1381
     * @return int                  Return integer <0 if KO, >0 if OK
1382
     */
1383
    public function update(User $user, $notrigger = 0, $mode = '')
1384
    {
1385
        global $langs;
1386
        $error = 0;
1387
1388
        dol_syslog(__METHOD__, LOG_DEBUG);
1389
1390
        // Clean parameters
1391
        if (isset($this->doc_type)) {
1392
            $this->doc_type = trim($this->doc_type);
1393
        }
1394
        if (isset($this->doc_ref)) {
1395
            $this->doc_ref = trim($this->doc_ref);
1396
        }
1397
        if (isset($this->fk_doc)) {
1398
            $this->fk_doc = (int) $this->fk_doc;
1399
        }
1400
        if (isset($this->fk_docdet)) {
1401
            $this->fk_docdet = (int) $this->fk_docdet;
1402
        }
1403
        if (isset($this->thirdparty_code)) {
1404
            $this->thirdparty_code = trim($this->thirdparty_code);
1405
        }
1406
        if (isset($this->subledger_account)) {
1407
            $this->subledger_account = trim($this->subledger_account);
1408
        }
1409
        if (isset($this->subledger_label)) {
1410
            $this->subledger_label = trim($this->subledger_label);
1411
        }
1412
        if (isset($this->numero_compte)) {
1413
            $this->numero_compte = trim($this->numero_compte);
1414
        }
1415
        if (isset($this->label_compte)) {
1416
            $this->label_compte = trim($this->label_compte);
1417
        }
1418
        if (isset($this->label_operation)) {
1419
            $this->label_operation = trim($this->label_operation);
1420
        }
1421
        if (isset($this->sens)) {
1422
            $this->sens = trim($this->sens);
1423
        }
1424
        if (isset($this->import_key)) {
1425
            $this->import_key = trim($this->import_key);
1426
        }
1427
        if (isset($this->code_journal)) {
1428
            $this->code_journal = trim($this->code_journal);
1429
        }
1430
        if (isset($this->journal_label)) {
1431
            $this->journal_label = trim($this->journal_label);
1432
        }
1433
        if (isset($this->piece_num)) {
1434
            $this->piece_num = (int) $this->piece_num;
1435
        }
1436
1437
        $result = $this->canModifyBookkeeping($this->id, $mode);
1438
        if ($result < 0) {
1439
            return -1;
1440
        } elseif ($result == 0) {
1441
            if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1442
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1443
            } else {
1444
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1445
            }
1446
            return -1;
1447
        }
1448
1449
        $this->debit = (float) price2num($this->debit, 'MT');
1450
        $this->credit = (float) price2num($this->credit, 'MT');
1451
        $this->montant = (float) price2num($this->montant, 'MT');
1452
1453
        // Check parameters
1454
        // Put here code to add a control on parameters values
1455
1456
        // Update request
1457
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . $mode . ' SET';
1458
        $sql .= ' doc_date = ' . (!isset($this->doc_date) || dol_strlen($this->doc_date) != 0 ? "'" . $this->db->idate($this->doc_date) . "'" : 'null') . ',';
1459
        $sql .= ' doc_type = ' . (isset($this->doc_type) ? "'" . $this->db->escape($this->doc_type) . "'" : "null") . ',';
1460
        $sql .= ' doc_ref = ' . (isset($this->doc_ref) ? "'" . $this->db->escape($this->doc_ref) . "'" : "null") . ',';
1461
        $sql .= ' fk_doc = ' . (isset($this->fk_doc) ? $this->fk_doc : "null") . ',';
1462
        $sql .= ' fk_docdet = ' . (isset($this->fk_docdet) ? $this->fk_docdet : "null") . ',';
1463
        $sql .= ' thirdparty_code = ' . (isset($this->thirdparty_code) ? "'" . $this->db->escape($this->thirdparty_code) . "'" : "null") . ',';
1464
        $sql .= ' subledger_account = ' . (isset($this->subledger_account) ? "'" . $this->db->escape($this->subledger_account) . "'" : "null") . ',';
1465
        $sql .= ' subledger_label = ' . (isset($this->subledger_label) ? "'" . $this->db->escape($this->subledger_label) . "'" : "null") . ',';
1466
        $sql .= ' numero_compte = ' . (isset($this->numero_compte) ? "'" . $this->db->escape($this->numero_compte) . "'" : "null") . ',';
1467
        $sql .= ' label_compte = ' . (isset($this->label_compte) ? "'" . $this->db->escape($this->label_compte) . "'" : "null") . ',';
1468
        $sql .= ' label_operation = ' . (isset($this->label_operation) ? "'" . $this->db->escape($this->label_operation) . "'" : "null") . ',';
1469
        $sql .= ' debit = ' . (isset($this->debit) ? $this->debit : "null") . ',';
1470
        $sql .= ' credit = ' . (isset($this->credit) ? $this->credit : "null") . ',';
1471
        $sql .= ' montant = ' . (isset($this->montant) ? $this->montant : "null") . ',';
1472
        $sql .= ' sens = ' . (isset($this->sens) ? "'" . $this->db->escape($this->sens) . "'" : "null") . ',';
1473
        $sql .= ' fk_user_author = ' . (isset($this->fk_user_author) ? $this->fk_user_author : "null") . ',';
1474
        $sql .= ' import_key = ' . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null") . ',';
1475
        $sql .= ' code_journal = ' . (isset($this->code_journal) ? "'" . $this->db->escape($this->code_journal) . "'" : "null") . ',';
1476
        $sql .= ' journal_label = ' . (isset($this->journal_label) ? "'" . $this->db->escape($this->journal_label) . "'" : "null") . ',';
1477
        $sql .= ' piece_num = ' . (isset($this->piece_num) ? $this->piece_num : "null");
1478
        $sql .= ' WHERE rowid=' . ((int) $this->id);
1479
1480
        $this->db->begin();
1481
1482
        $resql = $this->db->query($sql);
1483
        if (!$resql) {
1484
            $error++;
1485
            $this->errors[] = 'Error ' . $this->db->lasterror();
1486
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1487
        }
1488
1489
        // Uncomment this and change MYOBJECT to your own tag if you
1490
        // want this action calls a trigger.
1491
        //if (! $error && ! $notrigger) {
1492
1493
        // // Call triggers
1494
        // $result=$this->call_trigger('MYOBJECT_MODIFY',$user);
1495
        // if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
1496
        // // End call triggers
1497
        //}
1498
1499
        // Commit or rollback
1500
        if ($error) {
1501
            $this->db->rollback();
1502
1503
            return -1 * $error;
1504
        } else {
1505
            $this->db->commit();
1506
1507
            return 1;
1508
        }
1509
    }
1510
1511
    /**
1512
     * Update accounting movement
1513
     *
1514
     * @param  string  $piece_num      Piece num
1515
     * @param  string  $field          Field
1516
     * @param  string  $value          Value
1517
     * @param  string  $mode           Mode ('' or _tmp')
1518
     * @return int                     Return integer <0 if KO, >0 if OK
1519
     */
1520
    public function updateByMvt($piece_num = '', $field = '', $value = '', $mode = '')
1521
    {
1522
        $error = 0;
1523
1524
        $sql_filter = $this->getCanModifyBookkeepingSQL();
1525
        if (!isset($sql_filter)) {
1526
            return -1;
1527
        }
1528
1529
        $this->db->begin();
1530
1531
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . $mode;
1532
        $sql .= " SET " . $this->db->sanitize($field) . " = " . (is_numeric($value) ? ((float) $value) : "'" . $this->db->escape($value) . "'");
1533
        $sql .= " WHERE piece_num = " . ((int) $piece_num);
1534
        $sql .= $sql_filter;
1535
1536
        $resql = $this->db->query($sql);
1537
1538
        if (!$resql) {
1539
            $error++;
1540
            $this->errors[] = 'Error ' . $this->db->lasterror();
1541
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1542
        }
1543
        if ($error) {
1544
            $this->db->rollback();
1545
1546
            return -1 * $error;
1547
        } else {
1548
            $this->db->commit();
1549
1550
            return 1;
1551
        }
1552
    }
1553
1554
    /**
1555
     * Delete object in database
1556
     *
1557
     * @param User      $user       User that deletes
1558
     * @param int       $notrigger  0=launch triggers after, 1=disable triggers
1559
     * @param string    $mode       Mode ('' or 'tmp_')
1560
     * @return int                  Return integer <0 if KO, >0 if OK
1561
     */
1562
    public function delete(User $user, $notrigger = 0, $mode = '')
1563
    {
1564
        global $langs;
1565
1566
        dol_syslog(__METHOD__, LOG_DEBUG);
1567
1568
        $result = $this->canModifyBookkeeping($this->id, $mode);
1569
        if ($result < 0) {
1570
            return -1;
1571
        } elseif ($result == 0) {
1572
            if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1573
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1574
            } else {
1575
                $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1576
            }
1577
            return -1;
1578
        }
1579
1580
        $error = 0;
1581
1582
        $this->db->begin();
1583
1584
        // Uncomment this and change MYOBJECT to your own tag if you
1585
        // want this action calls a trigger.
1586
        //if (! $error && ! $notrigger) {
1587
1588
        // // Call triggers
1589
        // $result=$this->call_trigger('MYOBJECT_DELETE',$user);
1590
        // if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
1591
        // // End call triggers
1592
        //}
1593
1594
        if (!$error) {
1595
            $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element . $mode;
1596
            $sql .= ' WHERE rowid=' . ((int) $this->id);
1597
1598
            $resql = $this->db->query($sql);
1599
            if (!$resql) {
1600
                $error++;
1601
                $this->errors[] = 'Error ' . $this->db->lasterror();
1602
                dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1603
            }
1604
        }
1605
1606
        // Commit or rollback
1607
        if ($error) {
1608
            $this->db->rollback();
1609
1610
            return -1 * $error;
1611
        } else {
1612
            $this->db->commit();
1613
1614
            return 1;
1615
        }
1616
    }
1617
1618
    /**
1619
     * Delete bookkeeping by importkey
1620
     *
1621
     * @param  string       $importkey      Import key
1622
     * @param string $mode Mode
1623
     * @return int Result
1624
     */
1625
    public function deleteByImportkey($importkey, $mode = '')
1626
    {
1627
        $this->db->begin();
1628
1629
        $sql_filter = $this->getCanModifyBookkeepingSQL();
1630
        if (!isset($sql_filter)) {
1631
            return -1;
1632
        }
1633
1634
        // first check if line not yet in bookkeeping
1635
        $sql = "DELETE";
1636
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1637
        $sql .= " WHERE import_key = '" . $this->db->escape($importkey) . "'";
1638
        $sql .= $sql_filter;
1639
1640
        $resql = $this->db->query($sql);
1641
1642
        if (!$resql) {
1643
            $this->errors[] = "Error " . $this->db->lasterror();
1644
            dol_syslog(get_only_class($this) . "::delete Error " . $this->db->lasterror(), LOG_ERR);
1645
            $this->db->rollback();
1646
            return -1;
1647
        }
1648
1649
        $this->db->commit();
1650
        return 1;
1651
    }
1652
1653
    /**
1654
     * Delete bookkeeping by year
1655
     *
1656
     * @param  int    $delyear      Year to delete
1657
     * @param  string $journal      Journal to delete
1658
     * @param  string $mode         Mode
1659
     * @param  int    $delmonth     Month
1660
     * @return int                  Return integer <0 if KO, >0 if OK
1661
     */
1662
    public function deleteByYearAndJournal($delyear = 0, $journal = '', $mode = '', $delmonth = 0)
1663
    {
1664
        global $conf, $langs;
1665
1666
        if (empty($delyear) && empty($journal)) {
1667
            $this->error = 'ErrorOneFieldRequired';
1668
            return -1;
1669
        }
1670
        if (!empty($delmonth) && empty($delyear)) {
1671
            $this->error = 'YearRequiredIfMonthDefined';
1672
            return -2;
1673
        }
1674
1675
        $sql_filter = $this->getCanModifyBookkeepingSQL();
1676
        if (!isset($sql_filter)) {
1677
            return -1;
1678
        }
1679
1680
        $this->db->begin();
1681
1682
        // Delete record in bookkeeping
1683
        $sql = "DELETE";
1684
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1685
        $sql .= " WHERE 1 = 1";
1686
        $sql .= dolSqlDateFilter('doc_date', 0, $delmonth, $delyear);
1687
        if (!empty($journal)) {
1688
            $sql .= " AND code_journal = '" . $this->db->escape($journal) . "'";
1689
        }
1690
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1691
        // Exclusion of validated entries at the time of deletion
1692
        $sql .= " AND date_validated IS NULL";
1693
        $sql .= $sql_filter;
1694
1695
        // TODO: In a future we must forbid deletion if record is inside a closed fiscal period.
1696
1697
        $resql = $this->db->query($sql);
1698
1699
        if (!$resql) {
1700
            $this->errors[] = "Error " . $this->db->lasterror();
1701
            foreach ($this->errors as $errmsg) {
1702
                dol_syslog(get_only_class($this) . "::delete " . $errmsg, LOG_ERR);
1703
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1704
            }
1705
            $this->db->rollback();
1706
            return -1;
1707
        }
1708
1709
        $this->db->commit();
1710
        return 1;
1711
    }
1712
1713
    /**
1714
     * Delete bookkeeping by piece number
1715
     *
1716
     * @param   int     $piecenum   Piecenum to delete
1717
     * @param   string  $mode       Mode ('' or '_tmp')
1718
     * @return  int                 Result
1719
     */
1720
    public function deleteMvtNum($piecenum, $mode = '')
1721
    {
1722
        global $conf;
1723
1724
        $sql_filter = $this->getCanModifyBookkeepingSQL();
1725
        if (!isset($sql_filter)) {
1726
            return -1;
1727
        }
1728
1729
        $this->db->begin();
1730
1731
        // first check if line not yet in bookkeeping
1732
        $sql = "DELETE";
1733
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1734
        $sql .= " WHERE piece_num = " . (int) $piecenum;
1735
        $sql .= " AND date_validated IS NULL";      // For security, exclusion of validated entries at the time of deletion
1736
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1737
        $sql .= $sql_filter;
1738
1739
        $resql = $this->db->query($sql);
1740
1741
        if (!$resql) {
1742
            $this->errors[] = "Error " . $this->db->lasterror();
1743
            foreach ($this->errors as $errmsg) {
1744
                dol_syslog(get_only_class($this) . "::delete " . $errmsg, LOG_ERR);
1745
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1746
            }
1747
            $this->db->rollback();
1748
            return -1;
1749
        }
1750
1751
        $this->db->commit();
1752
        return 1;
1753
    }
1754
1755
    /**
1756
     * Load an object from its id and create a new one in database
1757
     *
1758
     * @param   User    $user       User making the clone
1759
     * @param   int     $fromid     Id of object to clone
1760
     * @return  int                 New id of clone
1761
     */
1762
    public function createFromClone(User $user, $fromid)
1763
    {
1764
        dol_syslog(__METHOD__, LOG_DEBUG);
1765
1766
        $error = 0;
1767
        $object = new BookKeeping($this->db);
1768
1769
        $this->db->begin();
1770
1771
        // Load source object
1772
        $object->fetch($fromid);
1773
        // Reset object
1774
        $object->id = 0;
1775
1776
        // Clear fields
1777
        // ...
1778
1779
        // Create clone
1780
        $object->context['createfromclone'] = 'createfromclone';
1781
        $result = $object->create($user);
1782
1783
        // Other options
1784
        if ($result < 0) {
1785
            $error++;
1786
            $this->errors = $object->errors;
1787
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1788
        }
1789
1790
        unset($object->context['createfromclone']);
1791
1792
        // End
1793
        if (!$error) {
1794
            $this->db->commit();
1795
1796
            return $object->id;
1797
        } else {
1798
            $this->db->rollback();
1799
1800
            return -1;
1801
        }
1802
    }
1803
1804
    /**
1805
     * Initialise object with example values
1806
     * Id must be 0 if object instance is a specimen
1807
     *
1808
     * @return int
1809
     */
1810
    public function initAsSpecimen()
1811
    {
1812
        global $user;
1813
1814
        $now = dol_now();
1815
1816
        $this->id = 0;
1817
        $this->doc_date = $now;
1818
        $this->doc_type = '';
1819
        $this->doc_ref = '';
1820
        $this->fk_doc = 0;
1821
        $this->fk_docdet = 0;
1822
        $this->thirdparty_code = 'CU001';
1823
        $this->subledger_account = '41100001';
1824
        $this->subledger_label = 'My customer company';
1825
        $this->numero_compte = '411';
1826
        $this->label_compte = 'Customer';
1827
        $this->label_operation = 'Sales of pea';
1828
        $this->debit = 99.9;
1829
        $this->credit = 0.0;
1830
        $this->amount = 0.0;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...es\BookKeeping::$amount has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1830
        /** @scrutinizer ignore-deprecated */ $this->amount = 0.0;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1831
        $this->sens = 'D';
1832
        $this->fk_user_author = $user->id;
1833
        $this->import_key = '20201027';
1834
        $this->code_journal = 'VT';
1835
        $this->journal_label = 'Journal de vente';
1836
        $this->piece_num = 1234;
1837
        $this->date_creation = $now;
1838
1839
        return 1;
1840
    }
1841
1842
    /**
1843
     * Load an accounting document into memory from database
1844
     *
1845
     * @param int $piecenum Accounting document to get
1846
     * @param string $mode Mode
1847
     * @return int Return integer <0 if KO, >0 if OK
1848
     */
1849
    public function fetchPerMvt($piecenum, $mode = '')
1850
    {
1851
        global $conf;
1852
1853
        $sql = "SELECT piece_num, doc_date, code_journal, journal_label, doc_ref, doc_type,";
1854
        $sql .= " date_creation, tms as date_modification, date_validated as date_validation, date_lim_reglement, import_key";
1855
        // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1856
        if ($mode != "_tmp") {
1857
            $sql .= ", date_export";
1858
        }
1859
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1860
        $sql .= " WHERE piece_num = " . ((int) $piecenum);
1861
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1862
1863
        dol_syslog(__METHOD__, LOG_DEBUG);
1864
        $result = $this->db->query($sql);
1865
        if ($result) {
1866
            $obj = $this->db->fetch_object($result);
1867
1868
            $this->piece_num = $obj->piece_num;
1869
            $this->code_journal = $obj->code_journal;
1870
            $this->journal_label = $obj->journal_label;
1871
            $this->doc_date = $this->db->jdate($obj->doc_date);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->doc_date) can also be of type string. However, the property $doc_date is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1872
            $this->doc_ref = $obj->doc_ref;
1873
            $this->doc_type = $obj->doc_type;
1874
            $this->date_creation = $this->db->jdate($obj->date_creation);
1875
            $this->date_modification = $this->db->jdate($obj->date_modification);
1876
            if ($mode != "_tmp") {
1877
                $this->date_export = $this->db->jdate($obj->date_export);
1878
            }
1879
            $this->date_validation = $this->db->jdate($obj->date_validation);
1880
            $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_lim_reglement) can also be of type string. However, the property $date_lim_reglement is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1881
            $this->import_key = $obj->import_key;
1882
        } else {
1883
            $this->error = "Error " . $this->db->lasterror();
1884
            dol_syslog(__METHOD__ . $this->error, LOG_ERR);
1885
            return -1;
1886
        }
1887
1888
        return 1;
1889
    }
1890
1891
    /**
1892
     * Return next movement number
1893
     *
1894
     * @param   string  $mode       Mode
1895
     * @return  int<1, max>|-1      Return next movement number or -1 if error
1896
     */
1897
    public function getNextNumMvt($mode = '')
1898
    {
1899
        global $conf;
1900
1901
        $sql = "SELECT MAX(piece_num)+1 as max FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1902
        $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1903
1904
        dol_syslog(get_only_class($this) . "::getNextNumMvt", LOG_DEBUG);
1905
1906
        $result = $this->db->query($sql);
1907
1908
        if ($result) {
1909
            $obj = $this->db->fetch_object($result);
1910
            if ($obj) {
1911
                $result = $obj->max;
1912
            }
1913
            if (empty($result)) {
1914
                $result = 1;
1915
            }
1916
            return $result;
1917
        } else {
1918
            $this->error = "Error " . $this->db->lasterror();
1919
            dol_syslog(get_only_class($this) . "::getNextNumMvt " . $this->error, LOG_ERR);
1920
            return -1;
1921
        }
1922
    }
1923
1924
    /**
1925
     * Load all accounting lines related to a given transaction ID $piecenum
1926
     *
1927
     * @param  int     $piecenum   Id of line to get
1928
     * @param  string  $mode       Mode ('' or '_tmp')
1929
     * @return int                 Return integer <0 if KO, >0 if OK
1930
     */
1931
    public function fetchAllPerMvt($piecenum, $mode = '')
1932
    {
1933
        global $conf;
1934
1935
        $sql = "SELECT rowid, doc_date, doc_type,";
1936
        $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1937
        $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1938
        $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
1939
        $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1940
        // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1941
        if ($mode != "_tmp") {
1942
            $sql .= ", date_export";
1943
        }
1944
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1945
        $sql .= " WHERE piece_num = " . ((int) $piecenum);
1946
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1947
1948
        dol_syslog(__METHOD__, LOG_DEBUG);
1949
        $result = $this->db->query($sql);
1950
        if ($result) {
1951
            while ($obj = $this->db->fetch_object($result)) {
1952
                $line = new BookKeepingLine($this->db);
1953
1954
                $line->id = $obj->rowid;
1955
1956
                $line->doc_date = $this->db->jdate($obj->doc_date);
1957
                $line->doc_type = $obj->doc_type;
1958
                $line->doc_ref = $obj->doc_ref;
1959
                $line->fk_doc = $obj->fk_doc;
1960
                $line->fk_docdet = $obj->fk_docdet;
1961
                $line->thirdparty_code = $obj->thirdparty_code;
1962
                $line->subledger_account = $obj->subledger_account;
1963
                $line->subledger_label = $obj->subledger_label;
1964
                $line->numero_compte = $obj->numero_compte;
1965
                $line->label_compte = $obj->label_compte;
1966
                $line->label_operation = $obj->label_operation;
1967
                $line->debit = $obj->debit;
1968
                $line->credit = $obj->credit;
1969
                $line->montant = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...okKeepingLine::$montant has been deprecated: see $amount ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1969
                /** @scrutinizer ignore-deprecated */ $line->montant = $obj->amount;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1970
                $line->amount = $obj->amount;
1971
                $line->sens = $obj->sens;
1972
                $line->code_journal = $obj->code_journal;
1973
                $line->journal_label = $obj->journal_label;
1974
                $line->piece_num = $obj->piece_num;
1975
                $line->date_creation = $obj->date_creation;
1976
                $line->date_modification = $obj->date_modification;
1977
                if ($mode != "_tmp") {
1978
                    $line->date_export = $obj->date_export;
1979
                }
1980
                $line->date_validation = $obj->date_validation;
1981
1982
                $this->linesmvt[] = $line;
1983
            }
1984
        } else {
1985
            $this->error = "Error " . $this->db->lasterror();
1986
            dol_syslog(__METHOD__ . $this->error, LOG_ERR);
1987
            return -1;
1988
        }
1989
1990
        return 1;
1991
    }
1992
1993
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1994
    /**
1995
     * Export bookkeeping
1996
     *
1997
     * @param   string  $model  Model
1998
     * @return  int             Result
1999
     */
2000
    public function export_bookkeeping($model = 'ebp')
2001
    {
2002
		// phpcs:enable
2003
        global $conf;
2004
2005
        $sql = "SELECT rowid, doc_date, doc_type,";
2006
        $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
2007
        $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
2008
        $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, piece_num,";
2009
        $sql .= " date_validated as date_validation";
2010
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
2011
        $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2012
2013
        dol_syslog(get_only_class($this) . "::export_bookkeeping", LOG_DEBUG);
2014
2015
        $resql = $this->db->query($sql);
2016
2017
        if ($resql) {
2018
            $this->linesexport = array();
2019
2020
            $num = $this->db->num_rows($resql);
2021
            while ($obj = $this->db->fetch_object($resql)) {
2022
                $line = new BookKeepingLine($this->db);
2023
2024
                $line->id = $obj->rowid;
2025
2026
                $line->doc_date = $this->db->jdate($obj->doc_date);
2027
                $line->doc_type = $obj->doc_type;
2028
                $line->doc_ref = $obj->doc_ref;
2029
                $line->fk_doc = $obj->fk_doc;
2030
                $line->fk_docdet = $obj->fk_docdet;
2031
                $line->thirdparty_code = $obj->thirdparty_code;
2032
                $line->subledger_account = $obj->subledger_account;
2033
                $line->subledger_label = $obj->subledger_label;
2034
                $line->numero_compte = $obj->numero_compte;
2035
                $line->label_compte = $obj->label_compte;
2036
                $line->label_operation = $obj->label_operation;
2037
                $line->debit = $obj->debit;
2038
                $line->credit = $obj->credit;
2039
                $line->montant = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...okKeepingLine::$montant has been deprecated: see $amount ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2039
                /** @scrutinizer ignore-deprecated */ $line->montant = $obj->amount;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2040
                $line->amount = $obj->amount;
2041
                $line->sens = $obj->sens;
2042
                $line->code_journal = $obj->code_journal;
2043
                $line->piece_num = $obj->piece_num;
2044
                $line->date_validation = $obj->date_validation;
2045
2046
                $this->linesexport[] = $line;
2047
            }
2048
            $this->db->free($resql);
2049
2050
            return $num;
2051
        } else {
2052
            $this->error = "Error " . $this->db->lasterror();
2053
            dol_syslog(get_only_class($this) . "::export_bookkeeping " . $this->error, LOG_ERR);
2054
            return -1;
2055
        }
2056
    }
2057
2058
    /**
2059
     * Transform transaction
2060
     *
2061
     * @param  int      $direction      If 0: tmp => real, if 1: real => tmp
2062
     * @param  string   $piece_num      Piece num = Transaction ref
2063
     * @return int                      int Return integer <0 if KO, >0 if OK
2064
     */
2065
    public function transformTransaction($direction = 0, $piece_num = '')
2066
    {
2067
        global $conf;
2068
2069
        $error = 0;
2070
2071
        $sql_filter = $this->getCanModifyBookkeepingSQL();
2072
2073
        if (!isset($sql_filter)) {
2074
            return -1;
2075
        }
2076
2077
        $this->db->begin();
2078
2079
        if ($direction == 0) {
2080
            $next_piecenum = $this->getNextNumMvt();
2081
            $now = dol_now();
2082
2083
            if ($next_piecenum < 0) {
2084
                $error++;
2085
            }
2086
2087
            if (!$error) {
2088
                // Delete if there is an empty line
2089
                $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element . '_tmp WHERE piece_num = ' . ((int) $piece_num) . ' AND entity = ' . ((int) $conf->entity) . " AND numero_compte IS NULL AND debit = 0 AND credit = 0";
2090
                $resql = $this->db->query($sql);
2091
                if (!$resql) {
2092
                    $error++;
2093
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2094
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2095
                }
2096
            }
2097
2098
            if (!$error) {
2099
                $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . ' (doc_date, doc_type,';
2100
                $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2101
                $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2102
                $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num, date_creation)';
2103
                $sql .= ' SELECT doc_date, doc_type,';
2104
                $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2105
                $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2106
                $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, ' . ((int) $next_piecenum) . ", '" . $this->db->idate($now) . "'";
2107
                $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . '_tmp WHERE piece_num = ' . ((int) $piece_num) . ' AND numero_compte IS NOT NULL AND entity = ' . ((int) $conf->entity);
2108
                $sql .= $sql_filter;
2109
                $resql = $this->db->query($sql);
2110
                if (!$resql) {
2111
                    $error++;
2112
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2113
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2114
                }
2115
            }
2116
2117
            if (!$error) {
2118
                $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element . '_tmp WHERE piece_num = ' . ((int) $piece_num) . ' AND entity = ' . ((int) $conf->entity);
2119
                $resql = $this->db->query($sql);
2120
                if (!$resql) {
2121
                    $error++;
2122
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2123
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2124
                }
2125
            }
2126
        } elseif ($direction == 1) {
2127
            if (!$error) {
2128
                $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element . '_tmp WHERE piece_num = ' . ((int) $piece_num) . ' AND entity = ' . ((int) $conf->entity);
2129
                $resql = $this->db->query($sql);
2130
                if (!$resql) {
2131
                    $error++;
2132
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2133
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2134
                }
2135
            }
2136
2137
            if (!$error) {
2138
                $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '_tmp (doc_date, doc_type,';
2139
                $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2140
                $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2141
                $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num)';
2142
                $sql .= ' SELECT doc_date, doc_type,';
2143
                $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2144
                $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2145
                $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num';
2146
                $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' WHERE piece_num = ' . ((int) $piece_num) . ' AND entity = ' . ((int) $conf->entity);
2147
                $sql .= $sql_filter;
2148
                $resql = $this->db->query($sql);
2149
                if (!$resql) {
2150
                    $error++;
2151
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2152
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2153
                }
2154
            }
2155
2156
            if (!$error) {
2157
                $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element . '_tmp WHERE piece_num = ' . ((int) $piece_num) . ' AND entity = ' . ((int) $conf->entity);
2158
                $sql .= $sql_filter;
2159
                $resql = $this->db->query($sql);
2160
                if (!$resql) {
2161
                    $error++;
2162
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2163
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2164
                }
2165
            }
2166
        }
2167
        if (!$error) {
2168
            $this->db->commit();
2169
            return 1;
2170
        } else {
2171
            $this->db->rollback();
2172
            return -1;
2173
        }
2174
        /*
2175
        $sql = "DELETE FROM ";
2176
        $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as ab";
2177
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2178
        $sql .= " AND aa.active = 1";
2179
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2180
        $sql .= " AND asy.rowid = " . ((int) $pcgver);
2181
        $sql .= " AND ab.entity IN (" . getEntity('accountancy') . ")";
2182
        $sql .= " ORDER BY account_number ASC";
2183
        */
2184
    }
2185
2186
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2187
    /**
2188
     * Return list of accounts with label by chart of accounts
2189
     *
2190
     * @param string     $selectid   Preselected chart of accounts
2191
     * @param string     $htmlname  Name of field in html form
2192
     * @param int       $showempty  Add an empty field
2193
     * @param array     $event      Event options
2194
     * @param int       $select_in  Value is a aa.rowid (0 default) or aa.account_number (1)
2195
     * @param int       $select_out Set value returned by select 0=rowid (default), 1=account_number
2196
     * @param string    $aabase     Set accounting_account base class to display empty=all or from 1 to 8 will display only account beginning by this number
2197
     * @return string|int   String with HTML select or -1 if KO
2198
     */
2199
    public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $aabase = '')
2200
    {
2201
		// phpcs:enable
2202
        global $conf;
2203
2204
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/accounting.lib.php';
2205
2206
        $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2207
2208
        $sql = "SELECT DISTINCT ab.numero_compte as account_number, aa.label as label, aa.rowid as rowid, aa.fk_pcg_version";
2209
        $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as ab";
2210
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2211
        $sql .= " AND aa.active = 1";
2212
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2213
        $sql .= " AND asy.rowid = " . ((int) $pcgver);
2214
        $sql .= " AND ab.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2215
        $sql .= " ORDER BY account_number ASC";
2216
2217
        dol_syslog(get_only_class($this) . "::select_account", LOG_DEBUG);
2218
        $resql = $this->db->query($sql);
2219
2220
        if (!$resql) {
2221
            $this->error = "Error " . $this->db->lasterror();
2222
            dol_syslog(get_only_class($this) . "::select_account " . $this->error, LOG_ERR);
2223
            return "-1";
2224
        }
2225
2226
        $out = ajax_combobox($htmlname, $event);
2227
2228
        $options = array();
2229
        $selected = null;
2230
2231
        while ($obj = $this->db->fetch_object($resql)) {
2232
            $label = length_accountg($obj->account_number) . ' - ' . $obj->label;
2233
2234
            $select_value_in = $obj->rowid;
2235
            $select_value_out = $obj->rowid;
2236
2237
            if ($select_in == 1) {
2238
                $select_value_in = $obj->account_number;
2239
            }
2240
            if ($select_out == 1) {
2241
                $select_value_out = $obj->account_number;
2242
            }
2243
2244
            // Remember guy's we store in database llx_facturedet the rowid of accounting_account and not the account_number
2245
            // Because same account_number can be share between different accounting_system and do have the same meaning
2246
            if (($selectid != '') && $selectid == $select_value_in) {
2247
                $selected = $select_value_out;
2248
            }
2249
2250
            $options[$select_value_out] = $label;
2251
        }
2252
2253
        $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300');
2254
        $this->db->free($resql);
2255
        return $out;
2256
    }
2257
2258
    /**
2259
     * Return id and description of a root accounting account.
2260
     * FIXME: This function takes the parent of parent to get the root account !
2261
     *
2262
     * @param   string  $account    Accounting account
2263
     * @return  array|int           Array with root account information (max 2 upper level), <0 if KO
2264
     */
2265
    public function getRootAccount($account = null)
2266
    {
2267
        global $conf;
2268
        $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2269
2270
        $sql  = "SELECT root.rowid, root.account_number, root.label as label,";
2271
        $sql .= " parent.rowid as parent_rowid, parent.account_number as parent_account_number, parent.label as parent_label";
2272
        $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_account as aa";
2273
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2274
        $sql .= " AND asy.rowid = " . ((int) $pcgver);
2275
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as parent ON aa.account_parent = parent.rowid AND parent.active = 1";
2276
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as root ON parent.account_parent = root.rowid AND root.active = 1";
2277
        $sql .= " WHERE aa.account_number = '" . $this->db->escape($account) . "'";
2278
        $sql .= " AND aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2279
2280
        dol_syslog(get_only_class($this) . "::select_account", LOG_DEBUG);
2281
        $resql = $this->db->query($sql);
2282
        if ($resql) {
2283
            $obj = '';
2284
            if ($this->db->num_rows($resql)) {
2285
                $obj = $this->db->fetch_object($resql);
2286
            }
2287
2288
            $result = array('id' => $obj->rowid, 'account_number' => $obj->account_number, 'label' => $obj->label);
2289
            return $result;
2290
        } else {
2291
            $this->error = "Error " . $this->db->lasterror();
2292
            dol_syslog(__METHOD__ . " " . $this->error, LOG_ERR);
2293
2294
            return -1;
2295
        }
2296
    }
2297
2298
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2299
    /**
2300
     * Description of accounting account
2301
     *
2302
     * @param   string  $account    Accounting account
2303
     * @return  string|int              Account desc or -1 if KO
2304
     */
2305
    public function get_compte_desc($account = null)
2306
    {
2307
		// phpcs:enable
2308
        global $conf;
2309
2310
        $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2311
        $sql  = "SELECT aa.account_number, aa.label, aa.rowid, aa.fk_pcg_version, cat.label as category";
2312
        $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_account as aa ";
2313
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2314
        $sql .= " AND aa.account_number = '" . $this->db->escape($account) . "'";
2315
        $sql .= " AND asy.rowid = " . ((int) $pcgver);
2316
        $sql .= " AND aa.active = 1";
2317
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_accounting_category as cat ON aa.fk_accounting_category = cat.rowid";
2318
        $sql .= " WHERE aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2319
2320
        dol_syslog(get_only_class($this) . "::select_account", LOG_DEBUG);
2321
        $resql = $this->db->query($sql);
2322
        if ($resql) {
2323
            $obj = '';
2324
            if ($this->db->num_rows($resql)) {
2325
                $obj = $this->db->fetch_object($resql);
2326
            }
2327
            if (empty($obj->category)) {
2328
                return $obj->label;
2329
            } else {
2330
                return $obj->label . ' (' . $obj->category . ')';
2331
            }
2332
        } else {
2333
            $this->error = "Error " . $this->db->lasterror();
2334
            dol_syslog(__METHOD__ . " " . $this->error, LOG_ERR);
2335
            return "-1";
2336
        }
2337
    }
2338
2339
    /**
2340
     * Get SQL string for check if the bookkeeping can be modified or deleted ? (cached)
2341
     *
2342
     * @param   string      $alias      Bookkeeping alias table
2343
     * @param   bool        $force      Force reload
2344
     * @return  string|null             SQL filter or null if error
2345
     */
2346
    public function getCanModifyBookkeepingSQL($alias = '', $force = false)
2347
    {
2348
        global $conf;
2349
2350
        $alias = trim($alias);
2351
        $alias = !empty($alias) && strpos($alias, '.') < 0 ? $alias . "." : $alias;
2352
2353
        if (!isset(self::$can_modify_bookkeeping_sql_cached[$alias]) || $force) {
2354
            $result = $this->loadFiscalPeriods($force, 'active');
2355
            if ($result < 0) {
2356
                return null;
2357
            }
2358
2359
            $sql_list = array();
2360
            if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2361
                foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2362
                    $sql_list[] = "('" . $this->db->idate($fiscal_period['date_start']) . "' <= " . $this->db->sanitize($alias) . "doc_date AND " . $this->db->sanitize($alias) . "doc_date <= '" . $this->db->idate($fiscal_period['date_end']) . "')";
2363
                }
2364
            }
2365
            $sqlsanitized = implode(' OR ', $sql_list);
2366
            self::$can_modify_bookkeeping_sql_cached[$alias] = !empty($sql_list) ? " AND (" . $sqlsanitized . ")" : "";
2367
        }
2368
2369
        return self::$can_modify_bookkeeping_sql_cached[$alias];
2370
    }
2371
2372
    /**
2373
     * Is the bookkeeping can be modified or deleted ?
2374
     *
2375
     * @param   int     $id     Bookkeeping ID
2376
     * @param   string  $mode   Mode ('' or 'tmp_')
2377
     * @return  int             Return integer <0 if KO, == 0 if No, == 1 if Yes
2378
     */
2379
    public function canModifyBookkeeping($id, $mode = '')
2380
    {
2381
        global $conf;
2382
2383
        if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2384
            $result = $this->loadFiscalPeriods(false, 'closed');
2385
2386
            if ($result < 0) {
2387
                return -1;
2388
            }
2389
2390
            $bookkeeping = new BookKeeping($this->db);
2391
            $result = $bookkeeping->fetch($id, null, $mode);
2392
            if ($result <= 0) {
2393
                return $result;
2394
            }
2395
2396
            if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2397
                foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2398
                    if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2399
                        return 0;
2400
                    }
2401
                }
2402
            }
2403
2404
            return 1;
2405
        } else {
2406
            $result = $this->loadFiscalPeriods(false, 'active');
2407
            if ($result < 0) {
2408
                return -1;
2409
            }
2410
2411
            $bookkeeping = new BookKeeping($this->db);
2412
            $result = $bookkeeping->fetch($id, null, $mode);
2413
2414
            if ($result <= 0) {
2415
                return $result;
2416
            }
2417
            if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2418
                foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2419
                    if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $bookkeeping->doc_date && (empty($fiscal_period['date_end']) || $bookkeeping->doc_date <= $fiscal_period['date_end'])) {
2420
                        return 1;
2421
                    }
2422
                }
2423
            }
2424
2425
            return 0;
2426
        }
2427
    }
2428
2429
    /**
2430
     * Is the bookkeeping date valid (on an open period or not on a closed period) ?
2431
     *
2432
     * @param   int     $date       Bookkeeping date
2433
     * @return  int                 Return integer <0 if KO, == 0 if No, == 1 if date is valid for a transfer
2434
     */
2435
    public function validBookkeepingDate($date)
2436
    {
2437
        global $conf;
2438
2439
        if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2440
            $result = $this->loadFiscalPeriods(false, 'closed');
2441
2442
            if ($result < 0) {
2443
                return -1;
2444
            }
2445
2446
            if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2447
                foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2448
                    if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2449
                        return 0;
2450
                    }
2451
                }
2452
            }
2453
2454
            return 1;
2455
        } else {
2456
            $result = $this->loadFiscalPeriods(false, 'active');
2457
            if ($result < 0) {
2458
                return -1;
2459
            }
2460
2461
            if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2462
                foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2463
                    if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $date && (empty($fiscal_period['date_end']) || $date <= $fiscal_period['date_end'])) {
2464
                        return 1;
2465
                    }
2466
                }
2467
            }
2468
2469
            return 0;
2470
        }
2471
    }
2472
2473
    /**
2474
     * Load list of active fiscal period
2475
     *
2476
     * @param   bool    $force      Force reload
2477
     * @param   string  $mode       active or closed ?
2478
     * @return  int                 Return integer <0 if KO, >0 if OK
2479
     */
2480
    public function loadFiscalPeriods($force = false, $mode = 'active')
2481
    {
2482
        global $conf;
2483
2484
        if ($mode == 'active') {
2485
            if (!isset($conf->cache['active_fiscal_period_cached']) || $force) {
2486
                $sql = "SELECT date_start, date_end";
2487
                $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2488
                $sql .= " WHERE entity = " . ((int) $conf->entity);
2489
                $sql .= " AND statut = 0";
2490
2491
                $resql = $this->db->query($sql);
2492
                if (!$resql) {
2493
                    $this->errors[] = $this->db->lasterror();
2494
                    return -1;
2495
                }
2496
2497
                $list = array();
2498
                while ($obj = $this->db->fetch_object($resql)) {
2499
                    $list[] = array(
2500
                        'date_start' => $this->db->jdate($obj->date_start),
2501
                        'date_end' => $this->db->jdate($obj->date_end),
2502
                    );
2503
                }
2504
                $conf->cache['active_fiscal_period_cached'] = $list;
2505
            }
2506
        }
2507
        if ($mode == 'closed') {
2508
            if (!isset($conf->cache['closed_fiscal_period_cached']) || $force) {
2509
                $sql = "SELECT date_start, date_end";
2510
                $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2511
                $sql .= " WHERE entity = " . ((int) $conf->entity);
2512
                $sql .= " AND statut = 1";
2513
2514
                $resql = $this->db->query($sql);
2515
                if (!$resql) {
2516
                    $this->errors[] = $this->db->lasterror();
2517
                    return -1;
2518
                }
2519
2520
                $list = array();
2521
                while ($obj = $this->db->fetch_object($resql)) {
2522
                    $list[] = array(
2523
                        'date_start' => $this->db->jdate($obj->date_start),
2524
                        'date_end' => $this->db->jdate($obj->date_end),
2525
                    );
2526
                }
2527
                $conf->cache['closed_fiscal_period_cached'] = $list;
2528
            }
2529
        }
2530
2531
        return 1;
2532
    }
2533
2534
    /**
2535
     * Get list of fiscal period
2536
     *
2537
     * @param   string  $filter     Filter
2538
     * @return  array<array{id:int,label:string,date_start:string,date_end:string,status:int}>|int          Return integer <0 if KO, Fiscal periods : [[id, date_start, date_end, label], ...]
2539
     */
2540
    public function getFiscalPeriods($filter = '')
2541
    {
2542
        global $conf;
2543
        $list = array();
2544
2545
        $sql = "SELECT rowid, label, date_start, date_end, statut";
2546
        $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2547
        $sql .= " WHERE entity = " . ((int) $conf->entity);
2548
        if (!empty($filter)) {
2549
            $sql .= " AND (" . $this->db->sanitize($filter, 1, 1, 1) . ')';
2550
        }
2551
        $sql .= $this->db->order('date_start', 'ASC');
2552
2553
        $resql = $this->db->query($sql);
2554
        if (!$resql) {
2555
            $this->errors[] = $this->db->lasterror();
2556
            return -1;
2557
        }
2558
2559
        while ($obj = $this->db->fetch_object($resql)) {
2560
            $list[$obj->rowid] = array(
2561
                'id' => (int) $obj->rowid,
2562
                'label' => $obj->label,
2563
                'date_start' => $this->db->jdate($obj->date_start),
2564
                'date_end' => $this->db->jdate($obj->date_end),
2565
                'status' => (int) $obj->statut,
2566
            );
2567
        }
2568
2569
        return $list;
2570
    }
2571
2572
    /**
2573
     * Get list of count by month into the fiscal period
2574
     *
2575
     * @param   int         $date_start     Date start
2576
     * @param   int         $date_end       Date end
2577
     * @return  array|int                   Return integer <0 if KO, Fiscal periods : [[id, date_start, date_end, label], ...]
2578
     */
2579
    public function getCountByMonthForFiscalPeriod($date_start, $date_end)
2580
    {
2581
        $total = 0;
2582
        $list = array();
2583
2584
        $sql = "SELECT YEAR(b.doc_date) as year";
2585
        for ($i = 1; $i <= 12; $i++) {
2586
            $sql .= ", SUM(" . $this->db->ifsql("MONTH(b.doc_date) = " . ((int) $i), "1", "0") . ") AS month" . ((int) $i);
2587
        }
2588
        $sql .= ", COUNT(b.rowid) as total";
2589
        $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as b";
2590
        $sql .= " WHERE b.doc_date >= '" . $this->db->idate($date_start) . "'";
2591
        $sql .= " AND b.doc_date <= '" . $this->db->idate($date_end) . "'";
2592
        $sql .= " AND b.entity IN (" . getEntity('bookkeeping', 0) . ")"; // We don't share object for accountancy
2593
2594
        // Get count for each month into the fiscal period
2595
        if (getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")) {
2596
            // TODO Analyse is done by finding record not into a closed period
2597
            // Loop on each closed period
2598
            $sql .= " AND b.doc_date BETWEEN 0 AND 0";
2599
        } else {
2600
            // Analyse closed record using the unitary flag/date on each record
2601
            $sql .= " AND date_validated IS NULL";
2602
        }
2603
2604
        $sql .= " GROUP BY YEAR(b.doc_date)";
2605
        $sql .= $this->db->order("year", 'ASC');
2606
2607
        dol_syslog(__METHOD__, LOG_DEBUG);
2608
        $resql = $this->db->query($sql);
2609
        if (!$resql) {
2610
            $this->errors[] = $this->db->lasterror();
2611
            return -1;
2612
        }
2613
2614
        while ($obj = $this->db->fetch_object($resql)) {
2615
            $total += (int) $obj->total;
2616
            $year_list = array(
2617
                'year' => (int) $obj->year,
2618
                'count' => array(),
2619
                'total' => (int) $obj->total,
2620
            );
2621
            for ($i = 1; $i <= 12; $i++) {
2622
                $year_list['count'][$i] = (int) $obj->{'month' . $i};
2623
            }
2624
2625
            $list[] = $year_list;
2626
        }
2627
2628
        $this->db->free($resql);
2629
2630
        return array(
2631
            'total' => $total,
2632
            'list' => $list,
2633
        );
2634
    }
2635
2636
    /**
2637
     *  Validate all movement between the specified dates
2638
     *
2639
     * @param   int     $date_start     Date start
2640
     * @param   int     $date_end       Date end
2641
     * @return  int                     int Return integer <0 if KO, >0 if OK
2642
     */
2643
    public function validateMovementForFiscalPeriod($date_start, $date_end)
2644
    {
2645
        global $conf;
2646
2647
        $now = dol_now();
2648
2649
        // Specify as export : update field date_validated on selected month/year
2650
        $sql = " UPDATE " . MAIN_DB_PREFIX . "accounting_bookkeeping";
2651
        $sql .= " SET date_validated = '" . $this->db->idate($now) . "'";
2652
        $sql .= " WHERE entity = " . ((int) $conf->entity);
2653
        $sql .= " AND DATE(doc_date) >= '" . $this->db->idate($date_start) . "'";
2654
        $sql .= " AND DATE(doc_date) <= '" . $this->db->idate($date_end) . "'";
2655
        $sql .= " AND date_validated IS NULL";
2656
2657
        dol_syslog(__METHOD__, LOG_DEBUG);
2658
        $resql = $this->db->query($sql);
2659
        if (!$resql) {
2660
            $this->errors[] = $this->db->lasterror();
2661
            return -1;
2662
        }
2663
2664
        return 1;
2665
    }
2666
2667
    /**
2668
     *  Define accounting result
2669
     *
2670
     * @param   int     $date_start     Date start
2671
     * @param   int     $date_end       Date end
2672
     * @return  string                  Accounting result
2673
     */
2674
    public function accountingResult($date_start, $date_end)
2675
    {
2676
        global $conf;
2677
2678
        $this->db->begin();
2679
2680
        $income_statement_amount = 0;
2681
2682
        if (getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT')) {
2683
            $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2684
2685
            $pcg_type_filter = array();
2686
            foreach ($accounting_groups_used_for_income_statement as $item) {
2687
                $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2688
            }
2689
2690
            $sql = 'SELECT';
2691
            $sql .= " t.numero_compte,";
2692
            $sql .= " aa.pcg_type,";
2693
            $sql .= " (SUM(t.credit) - SUM(t.debit)) as accounting_result";
2694
            $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
2695
            $sql .= ' LEFT JOIN  ' . MAIN_DB_PREFIX . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2696
            $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2697
            $sql .= " AND aa.entity = " . ((int) $conf->entity);
2698
            $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM ' . MAIN_DB_PREFIX . 'accounting_system WHERE rowid = ' . ((int) getDolGlobalInt('CHARTOFACCOUNTS')) . ')';
2699
            $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2700
            $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
2701
            $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
2702
            $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2703
2704
            $resql = $this->db->query($sql);
2705
            if (!$resql) {
2706
                $this->errors[] = 'Error ' . $this->db->lasterror();
2707
                dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2708
            } else {
2709
                while ($obj = $this->db->fetch_object($resql)) {
2710
                    $income_statement_amount += $obj->accounting_result;
2711
                }
2712
            }
2713
        }
2714
2715
        return (string) $income_statement_amount;
2716
    }
2717
2718
    /**
2719
     *  Close fiscal period
2720
     *
2721
     * @param   int     $fiscal_period_id               Fiscal year ID
2722
     * @param   int     $new_fiscal_period_id           New fiscal year ID
2723
     * @param   bool    $separate_auxiliary_account     Separate auxiliary account
2724
     * @param   bool    $generate_bookkeeping_records   Generate closure bookkeeping records
2725
     * @return  int                                     int Return integer <0 if KO, >0 if OK
2726
     */
2727
    public function closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account = false, $generate_bookkeeping_records = true)
2728
    {
2729
        global $conf, $langs, $user;
2730
2731
        // Current fiscal period
2732
        $fiscal_period_id = max(0, $fiscal_period_id);
2733
        if (empty($fiscal_period_id)) {
2734
            $langs->load('errors');
2735
            $this->errors[] = $langs->trans('ErrorBadParameters');
2736
            return -1;
2737
        }
2738
        $fiscal_period = new Fiscalyear($this->db);
2739
        $result = $fiscal_period->fetch($fiscal_period_id);
2740
        if ($result < 0) {
2741
            $this->error = $fiscal_period->error;
2742
            $this->errors = $fiscal_period->errors;
2743
            return -1;
2744
        } elseif (empty($fiscal_period->id)) {
2745
            $langs->loadLangs(array('errors', 'compta'));
2746
            $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2747
            return -1;
2748
        }
2749
2750
        // New fiscal period
2751
        $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2752
        if (empty($new_fiscal_period_id)) {
2753
            $langs->load('errors');
2754
            $this->errors[] = $langs->trans('ErrorBadParameters');
2755
            return -1;
2756
        }
2757
        $new_fiscal_period = new Fiscalyear($this->db);
2758
        $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2759
        if ($result < 0) {
2760
            $this->error = $new_fiscal_period->error;
2761
            $this->errors = $new_fiscal_period->errors;
2762
            return -1;
2763
        } elseif (empty($new_fiscal_period->id)) {
2764
            $langs->loadLangs(array('errors', 'compta'));
2765
            $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
2766
            return -1;
2767
        }
2768
2769
        $error = 0;
2770
        $this->db->begin();
2771
2772
        $fiscal_period->statut = Fiscalyear::STATUS_CLOSED;
2773
        $fiscal_period->status = Fiscalyear::STATUS_CLOSED; // Actually not used
2774
        $result = $fiscal_period->update($user);
2775
        if ($result < 0) {
2776
            $this->error = $fiscal_period->error;
2777
            $this->errors = $fiscal_period->errors;
2778
            $error++;
2779
        }
2780
2781
        if (!$error && !empty($generate_bookkeeping_records)) {
2782
            $journal_id = max(0, getDolGlobalString('ACCOUNTING_CLOSURE_DEFAULT_JOURNAL'));
2783
            if (empty($journal_id)) {
2784
                $langs->loadLangs(array('errors', 'accountancy'));
2785
                $this->errors[] = $langs->trans('ErrorBadParameters') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2786
                $error++;
2787
            }
2788
2789
            // Fetch journal
2790
            if (!$error) {
2791
                $journal = new AccountingJournal($this->db);
2792
                $result = $journal->fetch($journal_id);
2793
                if ($result < 0) {
2794
                    $this->error = $journal->error;
2795
                    $this->errors = $journal->errors;
2796
                    $error++;
2797
                } elseif ($result == 0) {
2798
                    $langs->loadLangs(array('errors', 'accountancy'));
2799
                    $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2800
                    $error++;
2801
                }
2802
            }
2803
2804
            if (!$error) {
2805
                $accounting_groups_used_for_balance_sheet_account = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_BALANCE_SHEET_ACCOUNT'))), 'strlen');
2806
                $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2807
2808
                $pcg_type_filter = array();
2809
                $tmp = array_merge($accounting_groups_used_for_balance_sheet_account, $accounting_groups_used_for_income_statement);
2810
                foreach ($tmp as $item) {
2811
                    $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2812
                }
2813
2814
                $sql = 'SELECT';
2815
                $sql .= " t.numero_compte,";
2816
                $sql .= " t.label_compte,";
2817
                if ($separate_auxiliary_account) {
2818
                    $sql .= " t.subledger_account,";
2819
                    $sql .= " t.subledger_label,";
2820
                }
2821
                $sql .= " aa.pcg_type,";
2822
                $sql .= " (SUM(t.credit) - SUM(t.debit)) as opening_balance";
2823
                $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
2824
                $sql .= ' LEFT JOIN  ' . MAIN_DB_PREFIX . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2825
                $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2826
                $sql .= " AND aa.entity = " . ((int) $conf->entity);
2827
                $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM ' . MAIN_DB_PREFIX . 'accounting_system WHERE rowid = ' . ((int) getDolGlobalInt('CHARTOFACCOUNTS')) . ')';
2828
                $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2829
                $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
2830
                $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
2831
                $sql .= ' GROUP BY t.numero_compte, t.label_compte, aa.pcg_type';
2832
                if ($separate_auxiliary_account) {
2833
                    $sql .= ' ,t.subledger_account, t.subledger_label';
2834
                }
2835
                $sql .= $this->db->order("t.numero_compte", "ASC");
2836
2837
                $resql = $this->db->query($sql);
2838
                if (!$resql) {
2839
                    $this->errors[] = 'Error ' . $this->db->lasterror();
2840
                    dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2841
2842
                    $error++;
2843
                } else {
2844
                    $now = dol_now();
2845
                    $income_statement_amount = 0;
2846
                    while ($obj = $this->db->fetch_object($resql)) {
2847
                        if (in_array($obj->pcg_type, $accounting_groups_used_for_income_statement)) {
2848
                            $income_statement_amount += $obj->opening_balance;
2849
                        } else {
2850
                            // Insert bookkeeping record for balance sheet account
2851
                            $mt = $obj->opening_balance;
2852
2853
                            $bookkeeping = new BookKeeping($this->db);
2854
                            $bookkeeping->doc_date = $new_fiscal_period->date_start;
2855
                            $bookkeeping->date_lim_reglement = 0;
2856
                            $bookkeeping->doc_ref = $new_fiscal_period->label;
2857
                            $bookkeeping->date_creation = $now;
2858
                            $bookkeeping->doc_type = 'closure';
2859
                            $bookkeeping->fk_doc = $new_fiscal_period->id;
2860
                            $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2861
                            $bookkeeping->thirdparty_code = '';
2862
2863
                            if ($separate_auxiliary_account) {
2864
                                $bookkeeping->subledger_account = $obj->subledger_account;
2865
                                $bookkeeping->subledger_label = $obj->subledger_label;
2866
                            } else {
2867
                                $bookkeeping->subledger_account = '';
2868
                                $bookkeeping->subledger_label = '';
2869
                            }
2870
2871
                            $bookkeeping->numero_compte = $obj->numero_compte;
2872
                            $bookkeeping->label_compte = $obj->label_compte;
2873
2874
                            $bookkeeping->label_operation = $new_fiscal_period->label;
2875
                            $bookkeeping->montant = $mt;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...s\BookKeeping::$montant has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2875
                            /** @scrutinizer ignore-deprecated */ $bookkeeping->montant = $mt;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2876
                            $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2877
                            $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2878
                            $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2879
                            $bookkeeping->code_journal = $journal->code;
2880
                            $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2881
                            $bookkeeping->fk_user_author = $user->id;
2882
                            $bookkeeping->entity = $conf->entity;
2883
2884
                            $result = $bookkeeping->create($user);
2885
                            if ($result < 0) {
2886
                                $this->error = $bookkeeping->error;
2887
                                $this->errors = $bookkeeping->errors;
2888
                                $error++;
2889
                                break;
2890
                            }
2891
                        }
2892
                    }
2893
2894
                    // Insert bookkeeping record for income statement
2895
                    if (!$error && $income_statement_amount != 0) {
2896
                        $mt = $income_statement_amount;
2897
                        $accountingaccount = new AccountingAccount($this->db);
2898
                        $accountingaccount->fetch(null, getDolGlobalString($income_statement_amount < 0 ? 'ACCOUNTING_RESULT_LOSS' : 'ACCOUNTING_RESULT_PROFIT'), true);
2899
2900
                        $bookkeeping = new BookKeeping($this->db);
2901
                        $bookkeeping->doc_date = $new_fiscal_period->date_start;
2902
                        $bookkeeping->date_lim_reglement = 0;
2903
                        $bookkeeping->doc_ref = $new_fiscal_period->label;
2904
                        $bookkeeping->date_creation = $now;
2905
                        $bookkeeping->doc_type = 'closure';
2906
                        $bookkeeping->fk_doc = $new_fiscal_period->id;
2907
                        $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2908
                        $bookkeeping->thirdparty_code = '';
2909
2910
                        if ($separate_auxiliary_account) {
2911
                            $bookkeeping->subledger_label = '';
2912
                            $bookkeeping->subledger_account = $obj->subledger_account;
2913
                            $bookkeeping->subledger_label = $obj->subledger_label;
2914
                        } else {
2915
                            $bookkeeping->subledger_account = '';
2916
                            $bookkeeping->subledger_label = '';
2917
                        }
2918
2919
                        $bookkeeping->numero_compte = $accountingaccount->account_number;
2920
                        $bookkeeping->label_compte = $accountingaccount->label;
2921
2922
                        $bookkeeping->label_operation = $new_fiscal_period->label;
2923
                        $bookkeeping->montant = $mt;
2924
                        $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2925
                        $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2926
                        $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2927
                        $bookkeeping->code_journal = $journal->code;
2928
                        $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2929
                        $bookkeeping->fk_user_author = $user->id;
2930
                        $bookkeeping->entity = $conf->entity;
2931
2932
                        $result = $bookkeeping->create($user);
2933
                        if ($result < 0) {
2934
                            $this->error = $bookkeeping->error;
2935
                            $this->errors = $bookkeeping->errors;
2936
                            $error++;
2937
                        }
2938
                    }
2939
                    $this->db->free($resql);
2940
                }
2941
            }
2942
        }
2943
2944
        if ($error) {
2945
            $this->db->rollback();
2946
            return -1;
2947
        } else {
2948
            $this->db->commit();
2949
            return 1;
2950
        }
2951
    }
2952
2953
    /**
2954
     *  Insert accounting reversal into the inventory journal of the new fiscal period
2955
     *
2956
     * @param   int     $fiscal_period_id       Fiscal year ID
2957
     * @param   int     $inventory_journal_id   Inventory journal ID
2958
     * @param   int     $new_fiscal_period_id   New fiscal year ID
2959
     * @param   int     $date_start             Date start
2960
     * @param   int     $date_end               Date end
2961
     * @return  int                             int Return integer <0 if KO, >0 if OK
2962
     */
2963
    public function insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
2964
    {
2965
        global $conf, $langs, $user;
2966
2967
        // Current fiscal period
2968
        $fiscal_period_id = max(0, $fiscal_period_id);
2969
        if (empty($fiscal_period_id)) {
2970
            $langs->load('errors');
2971
            $this->errors[] = $langs->trans('ErrorBadParameters');
2972
            return -1;
2973
        }
2974
        $fiscal_period = new Fiscalyear($this->db);
2975
        $result = $fiscal_period->fetch($fiscal_period_id);
2976
        if ($result < 0) {
2977
            $this->error = $fiscal_period->error;
2978
            $this->errors = $fiscal_period->errors;
2979
            return -1;
2980
        } elseif (empty($fiscal_period->id)) {
2981
            $langs->loadLangs(array('errors', 'compta'));
2982
            $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2983
            return -1;
2984
        }
2985
2986
        // New fiscal period
2987
        $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2988
        if (empty($new_fiscal_period_id)) {
2989
            $langs->load('errors');
2990
            $this->errors[] = $langs->trans('ErrorBadParameters');
2991
            return -1;
2992
        }
2993
        $new_fiscal_period = new Fiscalyear($this->db);
2994
        $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2995
        if ($result < 0) {
2996
            $this->error = $new_fiscal_period->error;
2997
            $this->errors = $new_fiscal_period->errors;
2998
            return -1;
2999
        } elseif (empty($new_fiscal_period->id)) {
3000
            $langs->loadLangs(array('errors', 'compta'));
3001
            $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
3002
            return -1;
3003
        }
3004
3005
        // Inventory journal
3006
        $inventory_journal_id = max(0, $inventory_journal_id);
3007
        if (empty($inventory_journal_id)) {
3008
            $langs->load('errors');
3009
            $this->errors[] = $langs->trans('ErrorBadParameters');
3010
            return -1;
3011
        }
3012
        // Fetch journal
3013
        $inventory_journal = new AccountingJournal($this->db);
3014
        $result = $inventory_journal->fetch($inventory_journal_id);
3015
        if ($result < 0) {
3016
            $this->error = $inventory_journal->error;
3017
            $this->errors = $inventory_journal->errors;
3018
            return -1;
3019
        } elseif ($result == 0) {
3020
            $langs->loadLangs(array('errors', 'accountancy'));
3021
            $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('InventoryJournal');
3022
            return -1;
3023
        }
3024
3025
        $error = 0;
3026
        $this->db->begin();
3027
3028
        $sql = 'SELECT t.rowid';
3029
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
3030
        $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
3031
        $sql .= " AND code_journal = '" . $this->db->escape($inventory_journal->code) . "'";
3032
        $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
3033
        $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
3034
        $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
3035
        $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
3036
3037
        $resql = $this->db->query($sql);
3038
        if (!$resql) {
3039
            $this->errors[] = 'Error ' . $this->db->lasterror();
3040
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3041
3042
            $error++;
3043
        } else {
3044
            $now = dol_now();
3045
            while ($obj = $this->db->fetch_object($resql)) {
3046
                $bookkeeping = new BookKeeping($this->db);
3047
                $result = $bookkeeping->fetch($obj->rowid);
3048
                if ($result < 0) {
3049
                    $this->error = $inventory_journal->error;
3050
                    $this->errors = $inventory_journal->errors;
3051
                    $error++;
3052
                    break;
3053
                } elseif ($result == 0) {
3054
                    $langs->loadLangs(array('errors', 'accountancy'));
3055
                    $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('LineId') . ': ' . $obj->rowid;
3056
                    $error++;
3057
                    break;
3058
                }
3059
3060
                $bookkeeping->id = 0;
3061
                $bookkeeping->doc_date = $new_fiscal_period->date_start;
3062
                $bookkeeping->doc_ref = $new_fiscal_period->label;
3063
                $bookkeeping->date_creation = $now;
3064
                $bookkeeping->doc_type = 'accounting_reversal';
3065
                $bookkeeping->fk_doc = $new_fiscal_period->id;
3066
                $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3067
3068
                $bookkeeping->montant = -$bookkeeping->montant;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...s\BookKeeping::$montant has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3068
                /** @scrutinizer ignore-deprecated */ $bookkeeping->montant = -$bookkeeping->montant;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
3069
                $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...s\BookKeeping::$montant has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3069
                $bookkeeping->sens = (/** @scrutinizer ignore-deprecated */ $bookkeeping->montant >= 0) ? 'C' : 'D';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
3070
                $old_debit = $bookkeeping->debit;
3071
                $bookkeeping->debit = $bookkeeping->credit;
3072
                $bookkeeping->credit = $old_debit;
3073
3074
                $bookkeeping->fk_user_author = $user->id;
3075
                $bookkeeping->entity = $conf->entity;
3076
3077
                $result = $bookkeeping->create($user);
3078
                if ($result < 0) {
3079
                    $this->error = $bookkeeping->error;
3080
                    $this->errors = $bookkeeping->errors;
3081
                    $error++;
3082
                    break;
3083
                }
3084
            }
3085
            $this->db->free($resql);
3086
        }
3087
3088
        if ($error) {
3089
            $this->db->rollback();
3090
            return -1;
3091
        } else {
3092
            $this->db->commit();
3093
            return 1;
3094
        }
3095
    }
3096
}
3097