Passed
Push — EXTRACT_CLASSES ( 231cec )
by Rafael
70:48
created

AccountingJournal   F

Complexity

Total Complexity 156

Size/Duplication

Total Lines 928
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 516
dl 0
loc 928
rs 2
c 0
b 0
f 0
wmc 156

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getLibType() 0 3 1
F getAssetData() 0 311 67
C exportCsv() 0 70 12
B fetch() 0 40 7
A getData() 0 36 6
F getNomUrl() 0 72 27
C LibType() 0 38 15
D writeIntoBookkeeping() 0 117 17
A __construct() 0 5 1
A getAccountingAccountInfos() 0 26 3

How to fix   Complexity   

Complex Class

Complex classes like AccountingJournal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AccountingJournal, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2017-2022  OpenDSI                     <[email protected]>
4
 * Copyright (C) 2024		MDW							<[email protected]>
5
 * Copyright (C) 2024       Frédéric France             <[email protected]>
6
 * Copyright (C) 2024       Rafael San José             <[email protected]>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Dolibarr\Code\Accountancy\Classes;
23
24
use Dolibarr\Code\Accountancy\Classes\AccountingAccount;
25
use Dolibarr\Code\Accountancy\Classes\BookKeeping;
26
use Dolibarr\Core\Base\CommonObject;
27
28
/**
29
 * \file        htdocs/accountancy/class/accountingjournal.class.php
30
 * \ingroup     Accountancy (Double entries)
31
 * \brief       File of class to manage accounting journals
32
 */
33
34
/**
35
 * Class to manage accounting journals
36
 */
37
class AccountingJournal extends CommonObject
38
{
39
    /**
40
     * @var string ID to identify managed object
41
     */
42
    public $element = 'accounting_journal';
43
44
    /**
45
     * @var string Name of table without prefix where object is stored
46
     */
47
    public $table_element = 'accounting_journal';
48
49
    /**
50
     * @var string Fieldname with ID of parent key if this field has a parent
51
     */
52
    public $fk_element = '';
53
54
    /**
55
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
56
     */
57
    public $picto = 'generic';
58
59
    /**
60
     * @var int ID
61
     */
62
    public $rowid;
63
64
    /**
65
     * @var string Accounting journal code
66
     */
67
    public $code;
68
69
    /**
70
     * @var string Accounting Journal label
71
     */
72
    public $label;
73
74
    /**
75
     * @var int 1:various operations, 2:sale, 3:purchase, 4:bank, 5:expense-report, 8:inventory, 9: has-new
76
     */
77
    public $nature;
78
79
    /**
80
     * @var int is active or not
81
     */
82
    public $active;
83
84
    /**
85
     * @var array       Accounting account cached
86
     */
87
    public static $accounting_account_cached = array();
88
89
    /**
90
     * @var array       Nature mapping
91
     */
92
    public static $nature_maps = array(
93
        1 => 'variousoperations',
94
        2 => 'sells',
95
        3 => 'purchases',
96
        4 => 'bank',
97
        5 => 'expensereports',
98
        8 => 'inventories',
99
        9 => 'hasnew',
100
    );
101
102
    /**
103
     * Constructor
104
     *
105
     * @param DoliDB $db Database handle
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Accountancy\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
106
     */
107
    public function __construct($db)
108
    {
109
        $this->db = $db;
110
111
        $this->ismultientitymanaged = 0;
112
    }
113
114
    /**
115
     * Load an object from database
116
     *
117
     * @param   int         $rowid          Id of record to load
118
     * @param   string|null $journal_code   Journal code
119
     * @return  int                         Return integer <0 if KO, Id of record if OK and found
120
     */
121
    public function fetch($rowid = 0, $journal_code = null)
122
    {
123
        global $conf;
124
125
        if ($rowid || $journal_code) {
126
            $sql = "SELECT rowid, code, label, nature, active";
127
            $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_journal";
128
            $sql .= " WHERE";
129
            if ($rowid) {
130
                $sql .= " rowid = " . ((int) $rowid);
131
            } elseif ($journal_code) {
132
                $sql .= " code = '" . $this->db->escape($journal_code) . "'";
133
                $sql .= " AND entity  = " . $conf->entity;
134
            }
135
136
            dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
137
            $result = $this->db->query($sql);
138
            if ($result) {
139
                $obj = $this->db->fetch_object($result);
140
141
                if ($obj) {
142
                    $this->id = $obj->rowid;
143
                    $this->rowid        = $obj->rowid;
144
145
                    $this->code         = $obj->code;
146
                    $this->ref          = $obj->code;
147
                    $this->label        = $obj->label;
148
                    $this->nature       = $obj->nature;
149
                    $this->active       = $obj->active;
150
151
                    return $this->id;
152
                } else {
153
                    return 0;
154
                }
155
            } else {
156
                $this->error = "Error " . $this->db->lasterror();
157
                $this->errors[] = "Error " . $this->db->lasterror();
158
            }
159
        }
160
        return -1;
161
    }
162
163
    /**
164
     * Return clickable name (with picto eventually)
165
     *
166
     * @param   int     $withpicto      0=No picto, 1=Include picto into link, 2=Only picto
167
     * @param   int     $withlabel      0=No label, 1=Include label of journal, 2=Include nature of journal
168
     * @param   int     $nourl          1=Disable url
169
     * @param   string  $moretitle      Add more text to title tooltip
170
     * @param   int     $notooltip      1=Disable tooltip
171
     * @return  string  String with URL
172
     */
173
    public function getNomUrl($withpicto = 0, $withlabel = 0, $nourl = 0, $moretitle = '', $notooltip = 0)
174
    {
175
        global $langs, $conf, $hookmanager;
176
177
        if (!empty($conf->dol_no_mouse_hover)) {
178
            $notooltip = 1; // Force disable tooltips
179
        }
180
181
        $result = '';
182
183
        $url = constant('BASE_URL') . '/accountancy/admin/journals_list.php?id=35';
184
185
        $label = '<u>' . $langs->trans("ShowAccountingJournal") . '</u>';
186
        if (!empty($this->code)) {
187
            $label .= '<br><b>' . $langs->trans('Code') . ':</b> ' . $this->code;
188
        }
189
        if (!empty($this->label)) {
190
            $label .= '<br><b>' . $langs->trans('Label') . ':</b> ' . $langs->transnoentities($this->label);
191
        }
192
        if ($moretitle) {
193
            $label .= ' - ' . $moretitle;
194
        }
195
196
        $linkclose = '';
197
        if (empty($notooltip)) {
198
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
199
                $label = $langs->trans("ShowAccountingJournal");
200
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
201
            }
202
            $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
203
            $linkclose .= ' class="classfortooltip"';
204
        }
205
206
        $linkstart = '<a href="' . $url . '"';
207
        $linkstart .= $linkclose . '>';
208
        $linkend = '</a>';
209
210
        if ($nourl) {
211
            $linkstart = '';
212
            $linkclose = '';
213
            $linkend = '';
214
        }
215
216
        $label_link = $this->code;
217
        if ($withlabel == 1 && !empty($this->label)) {
218
            $label_link .= ' - ' . ($nourl ? '<span class="opacitymedium">' : '') . $langs->transnoentities($this->label) . ($nourl ? '</span>' : '');
219
        }
220
        if ($withlabel == 2 && !empty($this->nature)) {
221
            $key = $langs->trans("AccountingJournalType" . $this->nature);
222
            $transferlabel = ($this->nature && $key != "AccountingJournalType" . strtoupper($langs->trans($this->nature)) ? $key : $this->label);
223
            $label_link .= ' - ' . ($nourl ? '<span class="opacitymedium">' : '') . $transferlabel . ($nourl ? '</span>' : '');
224
        }
225
226
        $result .= $linkstart;
227
        if ($withpicto) {
228
            $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);
229
        }
230
        if ($withpicto != 2) {
231
            $result .= $label_link;
232
        }
233
        $result .= $linkend;
234
235
        global $action;
236
        $hookmanager->initHooks(array('accountingjournaldao'));
237
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
238
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
239
        if ($reshook > 0) {
240
            $result = $hookmanager->resPrint;
241
        } else {
242
            $result .= $hookmanager->resPrint;
243
        }
244
        return $result;
245
    }
246
247
    /**
248
     *  Return the label of the status
249
     *
250
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
251
     *  @return string                 Label of status
252
     */
253
    public function getLibType($mode = 0)
254
    {
255
        return $this->LibType($this->nature, $mode);
256
    }
257
258
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
259
    /**
260
     *  Return type of an accounting journal
261
     *
262
     *  @param  int     $nature         Id type
263
     *  @param  int     $mode           0=label long, 1=label short
264
     *  @return string                  Label of type
265
     */
266
    public function LibType($nature, $mode = 0)
267
    {
268
		// phpcs:enable
269
        global $langs;
270
271
        $langs->loadLangs(array("accountancy"));
272
273
        if ($mode == 0) {
274
            $prefix = '';
275
            if ($nature == 9) {
276
                return $langs->trans('AccountingJournalType9');
277
            } elseif ($nature == 5) {
278
                return $langs->trans('AccountingJournalType5');
279
            } elseif ($nature == 4) {
280
                return $langs->trans('AccountingJournalType4');
281
            } elseif ($nature == 3) {
282
                return $langs->trans('AccountingJournalType3');
283
            } elseif ($nature == 2) {
284
                return $langs->trans('AccountingJournalType2');
285
            } elseif ($nature == 1) {
286
                return $langs->trans('AccountingJournalType1');
287
            }
288
        } elseif ($mode == 1) {
289
            if ($nature == 9) {
290
                return $langs->trans('AccountingJournalType9');
291
            } elseif ($nature == 5) {
292
                return $langs->trans('AccountingJournalType5');
293
            } elseif ($nature == 4) {
294
                return $langs->trans('AccountingJournalType4');
295
            } elseif ($nature == 3) {
296
                return $langs->trans('AccountingJournalType3');
297
            } elseif ($nature == 2) {
298
                return $langs->trans('AccountingJournalType2');
299
            } elseif ($nature == 1) {
300
                return $langs->trans('AccountingJournalType1');
301
            }
302
        }
303
        return "";
304
    }
305
306
307
    /**
308
     *  Get journal data
309
     *
310
     * @param   User            $user               User who get infos
311
     * @param   string          $type               Type data returned ('view', 'bookkeeping', 'csv')
312
     * @param   int             $date_start         Filter 'start date'
313
     * @param   int             $date_end           Filter 'end date'
314
     * @param   string          $in_bookkeeping     Filter 'in bookkeeping' ('already', 'notyet')
315
     * @return  array|int                           Return integer <0 if KO, >0 if OK
316
     */
317
    public function getData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Accountancy\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
318
    {
319
        global $hookmanager;
320
321
        // Clean parameters
322
        if (empty($type)) {
323
            $type = 'view';
324
        }
325
        if (empty($in_bookkeeping)) {
326
            $in_bookkeeping = 'notyet';
327
        }
328
329
        $data = array();
330
331
        $hookmanager->initHooks(array('accountingjournaldao'));
332
        $parameters = array('data' => &$data, 'user' => $user, 'type' => $type, 'date_start' => $date_start, 'date_end' => $date_end, 'in_bookkeeping' => $in_bookkeeping);
333
        $reshook = $hookmanager->executeHooks('getData', $parameters, $this); // Note that $action and $object may have been
334
        if ($reshook < 0) {
335
            $this->error = $hookmanager->error;
336
            $this->errors = $hookmanager->errors;
337
            return -1;
338
        } elseif (empty($reshook)) {
339
            switch ($this->nature) {
340
                case 1: // Various Journal
341
                    $data = $this->getAssetData($user, $type, $date_start, $date_end, $in_bookkeeping);
342
                    break;
343
                    //              case 2: // Sells Journal
344
                    //              case 3: // Purchases Journal
345
                    //              case 4: // Bank Journal
346
                    //              case 5: // Expense reports Journal
347
                    //              case 8: // Inventory Journal
348
                    //              case 9: // hasnew Journal
349
            }
350
        }
351
352
        return $data;
353
    }
354
355
    /**
356
     *  Get asset data for various journal
357
     *
358
     * @param   User            $user               User who get infos
359
     * @param   string          $type               Type data returned ('view', 'bookkeeping', 'csv')
360
     * @param   int             $date_start         Filter 'start date'
361
     * @param   int             $date_end           Filter 'end date'
362
     * @param   string          $in_bookkeeping     Filter 'in bookkeeping' ('already', 'notyet')
363
     * @return  array|int                           Return integer <0 if KO, >0 if OK
364
     */
365
    public function getAssetData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
366
    {
367
        global $conf, $langs;
368
369
        if (!isModEnabled('asset')) {
370
            return array();
371
        }
372
373
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/accounting.lib.php';
374
        require_once constant('DOL_DOCUMENT_ROOT') . '/asset/class/asset.class.php';
375
        require_once constant('DOL_DOCUMENT_ROOT') . '/asset/class/assetaccountancycodes.class.php';
376
        require_once constant('DOL_DOCUMENT_ROOT') . '/asset/class/assetdepreciationoptions.class.php';
377
378
        $langs->loadLangs(array("assets"));
379
380
        // Clean parameters
381
        if (empty($type)) {
382
            $type = 'view';
383
        }
384
        if (empty($in_bookkeeping)) {
385
            $in_bookkeeping = 'notyet';
386
        }
387
388
        $sql = "";
389
        $sql .= "SELECT ad.fk_asset AS rowid, a.ref AS asset_ref, a.label AS asset_label, a.acquisition_value_ht AS asset_acquisition_value_ht";
390
        $sql .= ", a.disposal_date AS asset_disposal_date, a.disposal_amount_ht AS asset_disposal_amount_ht, a.disposal_subject_to_vat AS asset_disposal_subject_to_vat";
391
        $sql .= ", ad.rowid AS depreciation_id, ad.depreciation_mode, ad.ref AS depreciation_ref, ad.depreciation_date, ad.depreciation_ht, ad.accountancy_code_debit, ad.accountancy_code_credit";
392
        $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation as ad";
393
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "asset as a ON a.rowid = ad.fk_asset";
394
        $sql .= " WHERE a.entity IN (" . getEntity('asset', 0) . ')'; // We don't share object for accountancy, we use source object sharing
395
        if ($in_bookkeeping == 'already') {
396
            $sql .= " AND EXISTS (SELECT iab.fk_docdet FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS iab WHERE iab.fk_docdet = ad.rowid AND doc_type = 'asset')";
397
        } elseif ($in_bookkeeping == 'notyet') {
398
            $sql .= " AND NOT EXISTS (SELECT iab.fk_docdet FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS iab WHERE iab.fk_docdet = ad.rowid AND doc_type = 'asset')";
399
        }
400
        $sql .= " AND ad.ref != ''"; // not reversal lines
401
        if ($date_start && $date_end) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $date_end of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $date_start of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
402
            $sql .= " AND ad.depreciation_date >= '" . $this->db->idate($date_start) . "' AND ad.depreciation_date <= '" . $this->db->idate($date_end) . "'";
403
        }
404
        // Define begin binding date
405
        if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
406
            $sql .= " AND ad.depreciation_date >= '" . $this->db->idate(getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) . "'";
407
        }
408
        $sql .= " ORDER BY ad.depreciation_date";
409
410
        dol_syslog(__METHOD__, LOG_DEBUG);
411
        $resql = $this->db->query($sql);
412
        if (!$resql) {
413
            $this->errors[] = $this->db->lasterror();
414
            return -1;
415
        }
416
417
        $pre_data = array(
418
            'elements' => array(),
419
        );
420
        while ($obj = $this->db->fetch_object($resql)) {
421
            if (!isset($pre_data['elements'][$obj->rowid])) {
422
                $pre_data['elements'][$obj->rowid] = array(
423
                    'ref' => $obj->asset_ref,
424
                    'label' => $obj->asset_label,
425
                    'acquisition_value_ht' => $obj->asset_acquisition_value_ht,
426
                    'depreciation' => array(),
427
                );
428
429
                // Disposal infos
430
                if (isset($obj->asset_disposal_date)) {
431
                    $pre_data['elements'][$obj->rowid]['disposal'] = array(
432
                        'date' => $this->db->jdate($obj->asset_disposal_date),
433
                        'amount' => $obj->asset_disposal_amount_ht,
434
                        'subject_to_vat' => !empty($obj->asset_disposal_subject_to_vat),
435
                    );
436
                }
437
            }
438
439
            $compta_debit = empty($obj->accountancy_code_debit) ? 'NotDefined' : $obj->accountancy_code_debit;
440
            $compta_credit = empty($obj->accountancy_code_credit) ? 'NotDefined' : $obj->accountancy_code_credit;
441
442
            $pre_data['elements'][$obj->rowid]['depreciation'][$obj->depreciation_id] = array(
443
                'date' => $this->db->jdate($obj->depreciation_date),
444
                'ref' => $obj->depreciation_ref,
445
                'lines' => array(
446
                    $compta_debit => -$obj->depreciation_ht,
447
                    $compta_credit => $obj->depreciation_ht,
448
                ),
449
            );
450
        }
451
452
        $disposal_ref = $langs->transnoentitiesnoconv('AssetDisposal');
453
        $journal = $this->code;
454
        $journal_label = $this->label;
455
        $journal_label_formatted = $langs->transnoentities($journal_label);
456
        $now = dol_now();
457
458
        $element_static = new Asset($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Accountancy\Classes\Asset was not found. Did you mean Asset? If so, make sure to prefix the type with \.
Loading history...
459
460
        $journal_data = array();
461
        foreach ($pre_data['elements'] as $pre_data_id => $pre_data_info) {
462
            $element_static->id = $pre_data_id;
463
            $element_static->ref = (string) $pre_data_info["ref"];
464
            $element_static->label = (string) $pre_data_info["label"];
465
            $element_static->acquisition_value_ht = $pre_data_info["acquisition_value_ht"];
466
            $element_link = $element_static->getNomUrl(1, 'with_label');
467
468
            $element_name_formatted_0 = dol_trunc($element_static->label, 16);
469
            $label_operation = $element_static->getNomUrl(0, 'label', 16);
470
471
            $element = array(
472
                'ref' => dol_trunc($element_static->ref, 16, 'right', 'UTF-8', 1),
473
                'error' => $pre_data_info['error'],
474
                'blocks' => array(),
475
            );
476
477
            // Depreciation lines
478
            //--------------------
479
            foreach ($pre_data_info['depreciation'] as $depreciation_id => $line) {
480
                $depreciation_ref = $line["ref"];
481
                $depreciation_date = $line["date"];
482
                $depreciation_date_formatted = dol_print_date($depreciation_date, 'day');
483
484
                // lines
485
                $blocks = array();
486
                foreach ($line['lines'] as $account => $mt) {
487
                    $account_infos = $this->getAccountingAccountInfos($account);
488
489
                    if ($type == 'view') {
490
                        $account_to_show = length_accounta($account);
491
                        if (($account_to_show == "") || $account_to_show == 'NotDefined') {
492
                            $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
493
                        }
494
495
                        $blocks[] = array(
496
                            'date' => $depreciation_date_formatted,
497
                            'piece' => $element_link,
498
                            'account_accounting' => $account_to_show,
499
                            'subledger_account' => '',
500
                            'label_operation' => $label_operation . ' - ' . $depreciation_ref,
501
                            'debit' => $mt < 0 ? price(-$mt) : '',
502
                            'credit' => $mt >= 0 ? price($mt) : '',
503
                        );
504
                    } elseif ($type == 'bookkeeping') {
505
                        if ($account_infos['found']) {
506
                            $blocks[] = array(
507
                                'doc_date' => $depreciation_date,
508
                                'date_lim_reglement' => '',
509
                                'doc_ref' => $element_static->ref,
510
                                'date_creation' => $now,
511
                                'doc_type' => 'asset',
512
                                'fk_doc' => $element_static->id,
513
                                'fk_docdet' => $depreciation_id, // Useless, can be several lines that are source of this record to add
514
                                'thirdparty_code' => '',
515
                                'subledger_account' => '',
516
                                'subledger_label' => '',
517
                                'numero_compte' => $account,
518
                                'label_compte' => $account_infos['label'],
519
                                'label_operation' => $element_name_formatted_0 . ' - ' . $depreciation_ref,
520
                                'montant' => $mt,
521
                                'sens' => $mt < 0 ? 'D' : 'C',
522
                                'debit' => $mt < 0 ? -$mt : 0,
523
                                'credit' => $mt >= 0 ? $mt : 0,
524
                                'code_journal' => $journal,
525
                                'journal_label' => $journal_label_formatted,
526
                                'piece_num' => '',
527
                                'import_key' => '',
528
                                'fk_user_author' => $user->id,
529
                                'entity' => $conf->entity,
530
                            );
531
                        }
532
                    } else { // $type == 'csv'
533
                        $blocks[] = array(
534
                            $depreciation_date,                                     // Date
535
                            $element_static->ref,                                   // Piece
536
                            $account_infos['code_formatted_1'],                     // AccountAccounting
537
                            $element_name_formatted_0 . ' - ' . $depreciation_ref,  // LabelOperation
538
                            $mt < 0 ? price(-$mt) : '',                             // Debit
539
                            $mt >= 0 ? price($mt) : '',                             // Credit
540
                        );
541
                    }
542
                }
543
                $element['blocks'][] = $blocks;
544
            }
545
546
            // Disposal line
547
            //--------------------
548
            if (!empty($pre_data_info['disposal'])) {
549
                $disposal_date = $pre_data_info['disposal']['date'];
550
551
                if (
552
                    (!($date_start && $date_end) || ($date_start <= $disposal_date && $disposal_date <= $date_end)) &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $date_start of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! $date_start && $date_...ING') <= $disposal_date, Probably Intended Meaning: ! $date_start && $date_e...NG') <= $disposal_date)
Loading history...
Bug Best Practice introduced by
The expression $date_end of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
553
                    (!getDolGlobalString('ACCOUNTING_DATE_START_BINDING') || getDolGlobalInt('ACCOUNTING_DATE_START_BINDING') <= $disposal_date)
554
                ) {
555
                    $disposal_amount = $pre_data_info['disposal']['amount'];
556
                    $disposal_subject_to_vat = $pre_data_info['disposal']['subject_to_vat'];
557
                    $disposal_date_formatted = dol_print_date($disposal_date, 'day');
558
                    $disposal_vat = getDolGlobalInt('ASSET_DISPOSAL_VAT') > 0 ? getDolGlobalInt('ASSET_DISPOSAL_VAT') : 20;
559
560
                    // Get accountancy codes
561
                    //---------------------------
562
                    require_once constant('DOL_DOCUMENT_ROOT') . '/asset/class/assetaccountancycodes.class.php';
563
                    $accountancy_codes = new AssetAccountancyCodes($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Accountanc...s\AssetAccountancyCodes was not found. Did you mean AssetAccountancyCodes? If so, make sure to prefix the type with \.
Loading history...
564
                    $result = $accountancy_codes->fetchAccountancyCodes($element_static->id);
565
                    if ($result < 0) {
566
                        $element['error'] = $accountancy_codes->errorsToString();
567
                    } else {
568
                        // Get last depreciation cumulative amount
569
                        $element_static->fetchDepreciationLines();
570
                        foreach ($element_static->depreciation_lines as $mode_key => $depreciation_lines) {
571
                            $accountancy_codes_list = $accountancy_codes->accountancy_codes[$mode_key];
572
573
                            if (!isset($accountancy_codes_list['value_asset_sold'])) {
574
                                continue;
575
                            }
576
577
                            $accountancy_code_value_asset_sold = empty($accountancy_codes_list['value_asset_sold']) ? 'NotDefined' : $accountancy_codes_list['value_asset_sold'];
578
                            $accountancy_code_depreciation_asset = empty($accountancy_codes_list['depreciation_asset']) ? 'NotDefined' : $accountancy_codes_list['depreciation_asset'];
579
                            $accountancy_code_asset = empty($accountancy_codes_list['asset']) ? 'NotDefined' : $accountancy_codes_list['asset'];
580
                            $accountancy_code_receivable_on_assignment = empty($accountancy_codes_list['receivable_on_assignment']) ? 'NotDefined' : $accountancy_codes_list['receivable_on_assignment'];
581
                            $accountancy_code_vat_collected = empty($accountancy_codes_list['vat_collected']) ? 'NotDefined' : $accountancy_codes_list['vat_collected'];
582
                            $accountancy_code_proceeds_from_sales = empty($accountancy_codes_list['proceeds_from_sales']) ? 'NotDefined' : $accountancy_codes_list['proceeds_from_sales'];
583
584
                            $last_cumulative_amount_ht = 0;
585
                            $depreciated_ids = array_keys($pre_data_info['depreciation']);
586
                            foreach ($depreciation_lines as $line) {
587
                                $last_cumulative_amount_ht = $line['cumulative_depreciation_ht'];
588
                                if (!in_array($line['id'], $depreciated_ids) && empty($line['bookkeeping']) && !empty($line['ref'])) {
589
                                    break;
590
                                }
591
                            }
592
593
                            $lines = array();
594
                            $lines[0][$accountancy_code_value_asset_sold] = -($element_static->acquisition_value_ht - $last_cumulative_amount_ht);
595
                            $lines[0][$accountancy_code_depreciation_asset] = -$last_cumulative_amount_ht;
596
                            $lines[0][$accountancy_code_asset] = $element_static->acquisition_value_ht;
597
598
                            $disposal_amount_vat = $disposal_subject_to_vat ? (float) price2num($disposal_amount * $disposal_vat / 100, 'MT') : 0;
599
                            $lines[1][$accountancy_code_receivable_on_assignment] = -($disposal_amount + $disposal_amount_vat);
600
                            if ($disposal_subject_to_vat) {
601
                                $lines[1][$accountancy_code_vat_collected] = $disposal_amount_vat;
602
                            }
603
                            $lines[1][$accountancy_code_proceeds_from_sales] = $disposal_amount;
604
605
                            foreach ($lines as $lines_block) {
606
                                $blocks = array();
607
                                foreach ($lines_block as $account => $mt) {
608
                                    $account_infos = $this->getAccountingAccountInfos($account);
609
610
                                    if ($type == 'view') {
611
                                        $account_to_show = length_accounta($account);
612
                                        if (($account_to_show == "") || $account_to_show == 'NotDefined') {
613
                                            $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
614
                                        }
615
616
                                        $blocks[] = array(
617
                                            'date' => $disposal_date_formatted,
618
                                            'piece' => $element_link,
619
                                            'account_accounting' => $account_to_show,
620
                                            'subledger_account' => '',
621
                                            'label_operation' => $label_operation . ' - ' . $disposal_ref,
622
                                            'debit' => $mt < 0 ? price(-$mt) : '',
623
                                            'credit' => $mt >= 0 ? price($mt) : '',
624
                                        );
625
                                    } elseif ($type == 'bookkeeping') {
626
                                        if ($account_infos['found']) {
627
                                            $blocks[] = array(
628
                                                'doc_date' => $disposal_date,
629
                                                'date_lim_reglement' => '',
630
                                                'doc_ref' => $element_static->ref,
631
                                                'date_creation' => $now,
632
                                                'doc_type' => 'asset',
633
                                                'fk_doc' => $element_static->id,
634
                                                'fk_docdet' => 0, // Useless, can be several lines that are source of this record to add
635
                                                'thirdparty_code' => '',
636
                                                'subledger_account' => '',
637
                                                'subledger_label' => '',
638
                                                'numero_compte' => $account,
639
                                                'label_compte' => $account_infos['label'],
640
                                                'label_operation' => $element_name_formatted_0 . ' - ' . $disposal_ref,
641
                                                'montant' => $mt,
642
                                                'sens' => $mt < 0 ? 'D' : 'C',
643
                                                'debit' => $mt < 0 ? -$mt : 0,
644
                                                'credit' => $mt >= 0 ? $mt : 0,
645
                                                'code_journal' => $journal,
646
                                                'journal_label' => $journal_label_formatted,
647
                                                'piece_num' => '',
648
                                                'import_key' => '',
649
                                                'fk_user_author' => $user->id,
650
                                                'entity' => $conf->entity,
651
                                            );
652
                                        }
653
                                    } else { // $type == 'csv'
654
                                        $blocks[] = array(
655
                                            $disposal_date,                                    // Date
656
                                            $element_static->ref,                              // Piece
657
                                            $account_infos['code_formatted_1'],                // AccountAccounting
658
                                            $element_name_formatted_0 . ' - ' . $disposal_ref, // LabelOperation
659
                                            $mt < 0 ? price(-$mt) : '',                        // Debit
660
                                            $mt >= 0 ? price($mt) : '',                        // Credit
661
                                        );
662
                                    }
663
                                }
664
                                $element['blocks'][] = $blocks;
665
                            }
666
                        }
667
                    }
668
                }
669
            }
670
671
            $journal_data[(int) $pre_data_id] = $element;
672
        }
673
        unset($pre_data);
674
675
        return $journal_data;
676
    }
677
678
    /**
679
     *  Write bookkeeping
680
     *
681
     * @param   User        $user               User who write in the bookkeeping
682
     * @param   array       $journal_data       Journal data to write in the bookkeeping
683
     *                                          $journal_data = array(
684
     *                                          id_element => array(
685
     *                                          'ref' => 'ref',
686
     *                                          'error' => '',
687
     *                                          'blocks' => array(
688
     *                                          pos_block => array(
689
     *                                          num_line => array(
690
     *                                          'doc_date' => '',
691
     *                                          'date_lim_reglement' => '',
692
     *                                          'doc_ref' => '',
693
     *                                          'date_creation' => '',
694
     *                                          'doc_type' => '',
695
     *                                          'fk_doc' => '',
696
     *                                          'fk_docdet' => '',
697
     *                                          'thirdparty_code' => '',
698
     *                                          'subledger_account' => '',
699
     *                                          'subledger_label' => '',
700
     *                                          'numero_compte' => '',
701
     *                                          'label_compte' => '',
702
     *                                          'label_operation' => '',
703
     *                                          'montant' => '',
704
     *                                          'sens' => '',
705
     *                                          'debit' => '',
706
     *                                          'credit' => '',
707
     *                                          'code_journal' => '',
708
     *                                          'journal_label' => '',
709
     *                                          'piece_num' => '',
710
     *                                          'import_key' => '',
711
     *                                          'fk_user_author' => '',
712
     *                                          'entity' => '',
713
     *                                          ),
714
     *                                          ),
715
     *                                          ),
716
     *                                          ),
717
     *                                          );
718
     * @param   int     $max_nb_errors          Nb error authorized before stop the process
719
     * @return  int                             Return integer <0 if KO, >0 if OK
720
     */
721
    public function writeIntoBookkeeping(User $user, &$journal_data = array(), $max_nb_errors = 10)
722
    {
723
        global $conf, $langs, $hookmanager;
724
725
        $error = 0;
726
727
        $hookmanager->initHooks(array('accountingjournaldao'));
728
        $parameters = array('journal_data' => &$journal_data);
729
        $reshook = $hookmanager->executeHooks('writeBookkeeping', $parameters, $this); // Note that $action and $object may have been
730
        if ($reshook < 0) {
731
            $this->error = $hookmanager->error;
732
            $this->errors = $hookmanager->errors;
733
            return -1;
734
        } elseif (empty($reshook)) {
735
            // Clean parameters
736
            $journal_data = is_array($journal_data) ? $journal_data : array();
737
738
            foreach ($journal_data as $element_id => $element) {
739
                $error_for_line = 0;
740
                $total_credit = 0;
741
                $total_debit = 0;
742
743
                $this->db->begin();
744
745
                if ($element['error'] == 'somelinesarenotbound') {
746
                    $error++;
747
                    $error_for_line++;
748
                    $this->errors[] = $langs->trans('ErrorInvoiceContainsLinesNotYetBounded', $element['ref']);
749
                }
750
751
                if (!$error_for_line) {
752
                    foreach ($element['blocks'] as $lines) {
753
                        foreach ($lines as $line) {
754
                            $bookkeeping = new BookKeeping($this->db);
755
                            $bookkeeping->doc_date = $line['doc_date'];
756
                            $bookkeeping->date_lim_reglement = $line['date_lim_reglement'];
757
                            $bookkeeping->doc_ref = $line['doc_ref'];
758
                            $bookkeeping->date_creation = $line['date_creation']; // not used
759
                            $bookkeeping->doc_type = $line['doc_type'];
760
                            $bookkeeping->fk_doc = $line['fk_doc'];
761
                            $bookkeeping->fk_docdet = $line['fk_docdet'];
762
                            $bookkeeping->thirdparty_code = $line['thirdparty_code'];
763
                            $bookkeeping->subledger_account = $line['subledger_account'];
764
                            $bookkeeping->subledger_label = $line['subledger_label'];
765
                            $bookkeeping->numero_compte = $line['numero_compte'];
766
                            $bookkeeping->label_compte = $line['label_compte'];
767
                            $bookkeeping->label_operation = $line['label_operation'];
768
                            $bookkeeping->montant = $line['montant'];
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Accountanc...s\BookKeeping::$montant has been deprecated: No more used (we have info into debit/credit and sens) ( Ignorable by Annotation )

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

768
                            /** @scrutinizer ignore-deprecated */ $bookkeeping->montant = $line['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...
769
                            $bookkeeping->sens = $line['sens'];
770
                            $bookkeeping->debit = $line['debit'];
771
                            $bookkeeping->credit = $line['credit'];
772
                            $bookkeeping->code_journal = $line['code_journal'];
773
                            $bookkeeping->journal_label = $line['journal_label'];
774
                            $bookkeeping->piece_num = $line['piece_num'];
775
                            $bookkeeping->import_key = $line['import_key'];
776
                            $bookkeeping->fk_user_author = $user->id;
777
                            $bookkeeping->entity = $conf->entity;
778
779
                            $total_debit += $bookkeeping->debit;
780
                            $total_credit += $bookkeeping->credit;
781
782
                            $result = $bookkeeping->create($user);
783
                            if ($result < 0) {
784
                                if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists') {   // Already exists
785
                                    $error++;
786
                                    $error_for_line++;
787
                                    $journal_data[$element_id]['error'] = 'alreadyjournalized';
788
                                } else {
789
                                    $error++;
790
                                    $error_for_line++;
791
                                    $journal_data[$element_id]['error'] = 'other';
792
                                    $this->errors[] = $bookkeeping->errorsToString();
793
                                }
794
                            }
795
                            //
796
                            //                          if (!$error_for_line && isModEnabled('asset') && $this->nature == 1 && $bookkeeping->fk_doc > 0) {
797
                            //                              // Set last cumulative depreciation
798
                            //                              require_once constant('DOL_DOCUMENT_ROOT') . '/asset/class/asset.class.php';
799
                            //                              $asset = new Asset($this->db);
800
                            //                              $result = $asset->setLastCumulativeDepreciation($bookkeeping->fk_doc);
801
                            //                              if ($result < 0) {
802
                            //                                  $error++;
803
                            //                                  $error_for_line++;
804
                            //                                  $journal_data[$element_id]['error'] = 'other';
805
                            //                                  $this->errors[] = $asset->errorsToString();
806
                            //                              }
807
                            //                          }
808
                        }
809
810
                        if ($error_for_line) {
811
                            break;
812
                        }
813
                    }
814
                }
815
816
                // Protection against a bug on lines before
817
                if (!$error_for_line && (price2num($total_debit, 'MT') != price2num($total_credit, 'MT'))) {
818
                    $error++;
819
                    $error_for_line++;
820
                    $journal_data[$element_id]['error'] = 'amountsnotbalanced';
821
                    $this->errors[] = 'Try to insert a non balanced transaction in book for ' . $element['blocks'] . '. Canceled. Surely a bug.';
822
                }
823
824
                if (!$error_for_line) {
825
                    $this->db->commit();
826
                } else {
827
                    $this->db->rollback();
828
829
                    if ($error >= $max_nb_errors) {
830
                        $this->errors[] = $langs->trans("ErrorTooManyErrorsProcessStopped");
831
                        break; // Break in the foreach
832
                    }
833
                }
834
            }
835
        }
836
837
        return $error ? -$error : 1;
838
    }
839
840
    /**
841
     *  Export journal CSV
842
     *  ISO and not UTF8 !
843
     *
844
     * @param   array           $journal_data           Journal data to write in the bookkeeping
845
     *                                                  $journal_data = array(
846
     *                                                  id_element => array(
847
     *                                                  'continue' => false,
848
     *                                                  'blocks' => array(
849
     *                                                  pos_block => array(
850
     *                                                  num_line => array(
851
     *                                                  data to write in the CSV line
852
     *                                                  ),
853
     *                                                  ),
854
     *                                                  ),
855
     *                                                  ),
856
     *                                                  );
857
     * @param   int             $search_date_end        Search date end
858
     * @param   string          $sep                    CSV separator
859
     * @return  int|string                              Return integer <0 if KO, >0 if OK
860
     */
861
    public function exportCsv(&$journal_data = array(), $search_date_end = 0, $sep = '')
862
    {
863
        global $conf, $langs, $hookmanager;
864
865
        if (empty($sep)) {
866
            $sep = getDolGlobalString('ACCOUNTING_EXPORT_SEPARATORCSV');
867
        }
868
        $out = '';
869
870
        // Hook
871
        $hookmanager->initHooks(array('accountingjournaldao'));
872
        $parameters = array('journal_data' => &$journal_data, 'search_date_end' => &$search_date_end, 'sep' => &$sep, 'out' => &$out);
873
        $reshook = $hookmanager->executeHooks('exportCsv', $parameters, $this); // Note that $action and $object may have been
874
        if ($reshook < 0) {
875
            $this->error = $hookmanager->error;
876
            $this->errors = $hookmanager->errors;
877
            return -1;
878
        } elseif (empty($reshook)) {
879
            // Clean parameters
880
            $journal_data = is_array($journal_data) ? $journal_data : array();
881
882
            // CSV header line
883
            $header = array();
884
            if ($this->nature == 4) {
885
                $header = array(
886
                    $langs->transnoentitiesnoconv("BankId"),
887
                    $langs->transnoentitiesnoconv("Date"),
888
                    $langs->transnoentitiesnoconv("PaymentMode"),
889
                    $langs->transnoentitiesnoconv("AccountAccounting"),
890
                    $langs->transnoentitiesnoconv("LedgerAccount"),
891
                    $langs->transnoentitiesnoconv("SubledgerAccount"),
892
                    $langs->transnoentitiesnoconv("Label"),
893
                    $langs->transnoentitiesnoconv("AccountingDebit"),
894
                    $langs->transnoentitiesnoconv("AccountingCredit"),
895
                    $langs->transnoentitiesnoconv("Journal"),
896
                    $langs->transnoentitiesnoconv("Note"),
897
                );
898
            } elseif ($this->nature == 5) {
899
                $header = array(
900
                    $langs->transnoentitiesnoconv("Date"),
901
                    $langs->transnoentitiesnoconv("Piece"),
902
                    $langs->transnoentitiesnoconv("AccountAccounting"),
903
                    $langs->transnoentitiesnoconv("LabelOperation"),
904
                    $langs->transnoentitiesnoconv("AccountingDebit"),
905
                    $langs->transnoentitiesnoconv("AccountingCredit"),
906
                );
907
            } elseif ($this->nature == 1) {
908
                $header = array(
909
                    $langs->transnoentitiesnoconv("Date"),
910
                    $langs->transnoentitiesnoconv("Piece"),
911
                    $langs->transnoentitiesnoconv("AccountAccounting"),
912
                    $langs->transnoentitiesnoconv("LabelOperation"),
913
                    $langs->transnoentitiesnoconv("AccountingDebit"),
914
                    $langs->transnoentitiesnoconv("AccountingCredit"),
915
                );
916
            }
917
918
            if (!empty($header)) {
919
                $out .= '"' . implode('"' . $sep . '"', $header) . '"' . "\n";
920
            }
921
            foreach ($journal_data as $element_id => $element) {
922
                foreach ($element['blocks'] as $lines) {
923
                    foreach ($lines as $line) {
924
                        $out .= '"' . implode('"' . $sep . '"', $line) . '"' . "\n";
925
                    }
926
                }
927
            }
928
        }
929
930
        return $out;
931
    }
932
933
    /**
934
     *  Get accounting account infos
935
     *
936
     * @param string    $account    Accounting account number
937
     * @return array                Accounting account infos
938
     */
939
    public function getAccountingAccountInfos($account)
940
    {
941
        if (!isset(self::$accounting_account_cached[$account])) {
942
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/accounting.lib.php';
943
            $accountingaccount = new AccountingAccount($this->db);
944
            $result = $accountingaccount->fetch(null, $account, true);
945
            if ($result > 0) {
946
                self::$accounting_account_cached[$account] = array(
947
                    'found' => true,
948
                    'label' => $accountingaccount->label,
949
                    'code_formatted_1' => length_accounta(html_entity_decode($account)),
950
                    'label_formatted_1' => mb_convert_encoding(dol_trunc($accountingaccount->label, 32), 'ISO-8859-1'),
951
                    'label_formatted_2' => dol_trunc($accountingaccount->label, 32),
952
                );
953
            } else {
954
                self::$accounting_account_cached[$account] = array(
955
                    'found' => false,
956
                    'label' => '',
957
                    'code_formatted_1' => length_accounta(html_entity_decode($account)),
958
                    'label_formatted_1' => '',
959
                    'label_formatted_2' => '',
960
                );
961
            }
962
        }
963
964
        return self::$accounting_account_cached[$account];
965
    }
966
}
967