Test Failed
Branch main (fda838)
by Rafael
50:22
created

BookKeeping::loadFiscalPeriods()   B

Complexity

Conditions 11
Paths 13

Size

Total Lines 52
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 34
nc 13
nop 2
dl 0
loc 52
rs 7.3166
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 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 DoliModules\Accounting\Model;
25
26
/**
27
 * \file        htdocs/accountancy/class/bookkeeping.class.php
28
 * \ingroup     Accountancy (Double entries)
29
 * \brief       File of class to manage Ledger (General Ledger and Subledger)
30
 */
31
32
use DoliCore\Base\GenericDocument;
33
34
// Class
35
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobjectline.class.php';
37
require_once DOL_DOCUMENT_ROOT . '/core/class/fiscalyear.class.php';
38
require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountingjournal.class.php';
39
require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountingaccount.class.php';
40
41
/**
42
 * Class to manage Ledger (General Ledger and Subledger)
43
 */
44
class BookKeeping extends GenericDocument
0 ignored issues
show
Deprecated Code introduced by
The class DoliCore\Base\GenericDocument has been deprecated: This class is only needed for compatibility with Dolibarr. ( Ignorable by Annotation )

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

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

829
                /** @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...
830
                $this->amount = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Accounting\Model\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

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

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

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

1834
        /** @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...
1835
        $this->sens = 'D';
1836
        $this->fk_user_author = $user->id;
1837
        $this->import_key = '20201027';
1838
        $this->code_journal = 'VT';
1839
        $this->journal_label = 'Journal de vente';
1840
        $this->piece_num = 1234;
1841
        $this->date_creation = $now;
1842
1843
        return 1;
1844
    }
1845
1846
    /**
1847
     * Load an accounting document into memory from database
1848
     *
1849
     * @param int $piecenum Accounting document to get
1850
     * @param string $mode Mode
1851
     * @return int Return integer <0 if KO, >0 if OK
1852
     */
1853
    public function fetchPerMvt($piecenum, $mode = '')
1854
    {
1855
        global $conf;
1856
1857
        $sql = "SELECT piece_num, doc_date, code_journal, journal_label, doc_ref, doc_type,";
1858
        $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1859
        // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1860
        if ($mode != "_tmp") {
1861
            $sql .= ", date_export";
1862
        }
1863
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1864
        $sql .= " WHERE piece_num = " . ((int) $piecenum);
1865
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1866
1867
        dol_syslog(__METHOD__, LOG_DEBUG);
1868
        $result = $this->db->query($sql);
1869
        if ($result) {
1870
            $obj = $this->db->fetch_object($result);
1871
1872
            $this->piece_num = $obj->piece_num;
1873
            $this->code_journal = $obj->code_journal;
1874
            $this->journal_label = $obj->journal_label;
1875
            $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...
1876
            $this->doc_ref = $obj->doc_ref;
1877
            $this->doc_type = $obj->doc_type;
1878
            $this->date_creation = $this->db->jdate($obj->date_creation);
1879
            $this->date_modification = $this->db->jdate($obj->date_modification);
1880
            if ($mode != "_tmp") {
1881
                $this->date_export = $this->db->jdate($obj->date_export);
1882
            }
1883
            $this->date_validation = $this->db->jdate($obj->date_validation);
1884
        } else {
1885
            $this->error = "Error " . $this->db->lasterror();
1886
            dol_syslog(__METHOD__ . $this->error, LOG_ERR);
1887
            return -1;
1888
        }
1889
1890
        return 1;
1891
    }
1892
1893
    /**
1894
     * Return next movement number
1895
     *
1896
     * @param   string  $mode       Mode
1897
     * @return  int<1, max>|-1      Return next movement number or -1 if error
1898
     */
1899
    public function getNextNumMvt($mode = '')
1900
    {
1901
        global $conf;
1902
1903
        $sql = "SELECT MAX(piece_num)+1 as max FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1904
        $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1905
1906
        dol_syslog(get_class($this) . "::getNextNumMvt", LOG_DEBUG);
1907
1908
        $result = $this->db->query($sql);
1909
1910
        if ($result) {
1911
            $obj = $this->db->fetch_object($result);
1912
            if ($obj) {
1913
                $result = $obj->max;
1914
            }
1915
            if (empty($result)) {
1916
                $result = 1;
1917
            }
1918
            return $result;
1919
        } else {
1920
            $this->error = "Error " . $this->db->lasterror();
1921
            dol_syslog(get_class($this) . "::getNextNumMvt " . $this->error, LOG_ERR);
1922
            return -1;
1923
        }
1924
    }
1925
1926
    /**
1927
     * Load all accounting lines related to a given transaction ID $piecenum
1928
     *
1929
     * @param  int     $piecenum   Id of line to get
1930
     * @param  string  $mode       Mode ('' or '_tmp')
1931
     * @return int                 Return integer <0 if KO, >0 if OK
1932
     */
1933
    public function fetchAllPerMvt($piecenum, $mode = '')
1934
    {
1935
        global $conf;
1936
1937
        $sql = "SELECT rowid, doc_date, doc_type,";
1938
        $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1939
        $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1940
        $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
1941
        $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1942
        // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1943
        if ($mode != "_tmp") {
1944
            $sql .= ", date_export";
1945
        }
1946
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . $mode;
1947
        $sql .= " WHERE piece_num = " . ((int) $piecenum);
1948
        $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1949
1950
        dol_syslog(__METHOD__, LOG_DEBUG);
1951
        $result = $this->db->query($sql);
1952
        if ($result) {
1953
            while ($obj = $this->db->fetch_object($result)) {
1954
                $line = new BookKeepingLine($this->db);
1955
1956
                $line->id = $obj->rowid;
1957
1958
                $line->doc_date = $this->db->jdate($obj->doc_date);
1959
                $line->doc_type = $obj->doc_type;
1960
                $line->doc_ref = $obj->doc_ref;
1961
                $line->fk_doc = $obj->fk_doc;
1962
                $line->fk_docdet = $obj->fk_docdet;
1963
                $line->thirdparty_code = $obj->thirdparty_code;
1964
                $line->subledger_account = $obj->subledger_account;
1965
                $line->subledger_label = $obj->subledger_label;
1966
                $line->numero_compte = $obj->numero_compte;
1967
                $line->label_compte = $obj->label_compte;
1968
                $line->label_operation = $obj->label_operation;
1969
                $line->debit = $obj->debit;
1970
                $line->credit = $obj->credit;
1971
                $line->montant = $obj->amount;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Accounting\M...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

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

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

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

3067
                $bookkeeping->montant = -/** @scrutinizer ignore-deprecated */ $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...
3068
                $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Accounting\Model\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
                $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...
3069
                $old_debit = $bookkeeping->debit;
3070
                $bookkeeping->debit = $bookkeeping->credit;
3071
                $bookkeeping->credit = $old_debit;
3072
3073
                $bookkeeping->fk_user_author = $user->id;
3074
                $bookkeeping->entity = $conf->entity;
3075
3076
                $result = $bookkeeping->create($user);
3077
                if ($result < 0) {
3078
                    $this->error = $bookkeeping->error;
3079
                    $this->errors = $bookkeeping->errors;
3080
                    $error++;
3081
                    break;
3082
                }
3083
            }
3084
            $this->db->free($resql);
3085
        }
3086
3087
        if ($error) {
3088
            $this->db->rollback();
3089
            return -1;
3090
        } else {
3091
            $this->db->commit();
3092
            return 1;
3093
        }
3094
    }
3095
}
3096