Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

BookKeeping::createFromClone()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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

827
                /** @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...
828
                $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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
3066
                $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

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