dolibarr 23.0.3
accountingjournal.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017-2022 OpenDSI <support@open-dsi.fr>
3 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
4 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2024 Alexandre Janniaux <alexandre.janniaux@gmail.com>
6 * Copyright (C) 2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
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
32{
36 public $element = 'accounting_journal';
37
41 public $table_element = 'accounting_journal';
42
46 public $fk_element = '';
47
51 public $picto = 'generic';
52
56 public $rowid;
57
61 public $code;
62
66 public $label;
67
71 public $nature;
72
76 public $active;
77
81 public static $accounting_account_cached = array();
82
86 public static $nature_maps = array(
87 1 => 'variousoperations',
88 2 => 'sells',
89 3 => 'purchases',
90 4 => 'bank',
91 5 => 'expensereports',
92 8 => 'inventories',
93 9 => 'hasnew',
94 );
95
101 public function __construct($db)
102 {
103 $this->db = $db;
104
105 $this->ismultientitymanaged = 0;
106 }
107
114 public function create($user)
115 {
116 $valid_nature = array(1, 2, 3, 4, 5, 8, 9);
117 if (!in_array((int) $this->nature, $valid_nature)) {
118 $this->error = get_class($this)."::Create Error invalid field nature '" . strval($this->nature) . "'";
119 dol_syslog($this->error, LOG_ERR);
120 return -1;
121 }
122
123 $sql = "INSERT INTO ".MAIN_DB_PREFIX."accounting_journal";
124 $sql .= " (entity, code, label, nature, active)";
125 $sql .= " VALUES ("
126 . ((int) $this->entity) .",'"
127 . $this->db->escape($this->code) ."','"
128 . $this->db->escape($this->label) ."',"
129 . ((int) $this->nature) .","
130 . ((int) $this->active) .")";
131
132 dol_syslog(get_class($this)."::create", LOG_DEBUG);
133 $resql = $this->db->query($sql);
134 if (!$resql) {
135 $this->error = get_class($this)."::Create Error: " . $this->db->lasterror();
136 dol_syslog($this->error, LOG_ERR);
137 return -1;
138 }
139
140 $id = $this->db->last_insert_id(MAIN_DB_PREFIX."accounting_journal");
141 if ($id <= 0) {
142 $this->error = get_class($this)."::Create Error " . $id . ": " . $this->db->lasterror();
143 dol_syslog($this->error, LOG_ERR);
144 return -2;
145 }
146
147 $this->id = $id;
148 $this->rowid = $id;
149 return $id;
150 }
151
159 public function fetch($rowid = 0, $journal_code = null)
160 {
161 global $conf;
162
163 if ($rowid || $journal_code) {
164 $sql = "SELECT rowid, code, label, nature, active";
165 $sql .= " FROM ".MAIN_DB_PREFIX."accounting_journal";
166 $sql .= " WHERE";
167 if ($rowid) {
168 $sql .= " rowid = ".((int) $rowid);
169 } elseif ($journal_code) {
170 $sql .= " code = '".$this->db->escape($journal_code)."'";
171 $sql .= " AND entity = ".$conf->entity;
172 }
173
174 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
175 $result = $this->db->query($sql);
176 if ($result) {
177 $obj = $this->db->fetch_object($result);
178
179 if ($obj) {
180 $this->id = $obj->rowid;
181 $this->rowid = $obj->rowid;
182
183 $this->code = $obj->code;
184 $this->ref = $obj->code;
185 $this->label = $obj->label;
186 $this->nature = $obj->nature;
187 $this->active = $obj->active;
188
189 return $this->id;
190 } else {
191 return 0;
192 }
193 } else {
194 $this->error = "Error ".$this->db->lasterror();
195 $this->errors[] = "Error ".$this->db->lasterror();
196 }
197 }
198 return -1;
199 }
200
211 public function getNomUrl($withpicto = 0, $withlabel = 0, $nourl = 0, $moretitle = '', $notooltip = 0)
212 {
213 global $langs, $conf, $hookmanager;
214
215 if (!empty($conf->dol_no_mouse_hover)) {
216 $notooltip = 1; // Force disable tooltips
217 }
218
219 $result = '';
220
221 $url = dolBuildUrl(DOL_URL_ROOT.'/accountancy/admin/journals_list.php', ['id' => 35]);
222
223 $label = '<u>'.$langs->trans("ShowAccountingJournal").'</u>';
224 if (!empty($this->code)) {
225 $label .= '<br><b>'.$langs->trans('Code').':</b> '.$this->code;
226 }
227 if (!empty($this->label)) {
228 $label .= '<br><b>'.$langs->trans('Label').':</b> '.$langs->transnoentities($this->label);
229 }
230 if ($moretitle) {
231 $label .= ' - '.$moretitle;
232 }
233
234 $linkclose = '';
235 if (empty($notooltip)) {
236 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
237 $label = $langs->trans("ShowAccountingJournal");
238 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
239 }
240 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
241 $linkclose .= ' class="classfortooltip"';
242 }
243
244 $linkstart = '<a href="'.$url.'"';
245 $linkstart .= $linkclose.'>';
246 $linkend = '</a>';
247
248 if ($nourl) {
249 $linkstart = '';
250 $linkclose = '';
251 $linkend = '';
252 }
253
254 $label_link = $this->code;
255 if ($withlabel == 1 && !empty($this->label)) {
256 $label_link .= ' - '.($nourl ? '<span class="opacitymedium">' : '').$langs->transnoentities($this->label).($nourl ? '</span>' : '');
257 }
258 if ($withlabel == 2 && !empty($this->nature)) {
259 $key = $langs->trans("AccountingJournalType".$this->nature);
260 $transferlabel = ($key != "AccountingJournalType".strtoupper($langs->trans((string) $this->nature)) ? $key : $this->label);
261 $label_link .= ' - '.($nourl ? '<span class="opacitymedium">' : '').$transferlabel.($nourl ? '</span>' : '');
262 }
263
264 $result .= $linkstart;
265 if ($withpicto) {
266 $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);
267 }
268 if ($withpicto != 2) {
269 $result .= $label_link;
270 }
271 $result .= $linkend;
272
273 global $action;
274 $hookmanager->initHooks(array('accountingjournaldao'));
275 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
276 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
277 if ($reshook > 0) {
278 $result = $hookmanager->resPrint;
279 } else {
280 $result .= $hookmanager->resPrint;
281 }
282 return $result;
283 }
284
291 public function getLibType($mode = 0)
292 {
293 return $this->LibType($this->nature, $mode);
294 }
295
296 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
304 public function LibType($nature, $mode = 0)
305 {
306 // phpcs:enable
307 global $langs;
308
309 $langs->loadLangs(array("accountancy"));
310
311 if ($mode == 0) {
312 $prefix = '';
313 if ($nature == 9) {
314 return $langs->trans('AccountingJournalType9');
315 } elseif ($nature == 5) {
316 return $langs->trans('AccountingJournalType5');
317 } elseif ($nature == 4) {
318 return $langs->trans('AccountingJournalType4');
319 } elseif ($nature == 3) {
320 return $langs->trans('AccountingJournalType3');
321 } elseif ($nature == 2) {
322 return $langs->trans('AccountingJournalType2');
323 } elseif ($nature == 1) {
324 return $langs->trans('AccountingJournalType1');
325 }
326 } elseif ($mode == 1) {
327 if ($nature == 9) {
328 return $langs->trans('AccountingJournalType9');
329 } elseif ($nature == 5) {
330 return $langs->trans('AccountingJournalType5');
331 } elseif ($nature == 4) {
332 return $langs->trans('AccountingJournalType4');
333 } elseif ($nature == 3) {
334 return $langs->trans('AccountingJournalType3');
335 } elseif ($nature == 2) {
336 return $langs->trans('AccountingJournalType2');
337 } elseif ($nature == 1) {
338 return $langs->trans('AccountingJournalType1');
339 }
340 }
341 return "";
342 }
343
344
355 public function getData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
356 {
357 global $hookmanager;
358
359 // Clean parameters
360 if (empty($type)) {
361 $type = 'view';
362 }
363 if (empty($in_bookkeeping)) {
364 $in_bookkeeping = 'notyet';
365 }
366
367 $data = array();
368
369 $hookmanager->initHooks(array('accountingjournaldao'));
370 $parameters = array('data' => &$data, 'user' => $user, 'type' => $type, 'date_start' => $date_start, 'date_end' => $date_end, 'in_bookkeeping' => $in_bookkeeping);
371 $reshook = $hookmanager->executeHooks('getData', $parameters, $this); // Note that $action and $object may have been
372 if ($reshook < 0) {
373 $this->error = $hookmanager->error;
374 $this->errors = $hookmanager->errors;
375 return -1;
376 } elseif (empty($reshook)) {
377 switch ($this->nature) {
378 case 1: // Various Journal
379 if (isModEnabled('asset') && !getDolGlobalInt('ACCOUNTING_DISABLE_TRANSFER_ON_ASSETS')) {
380 $tmp = $this->getAssetData($user, $type, $date_start, $date_end, $in_bookkeeping);
381 if (is_array($tmp)) {
382 $data = array_merge($data, $tmp);
383 }
384 }
385 if (isModEnabled('invoice') && !getDolGlobalInt('ACCOUNTING_DISABLE_TRANSFER_ON_DISCOUNTS')) {
386 $tmp = $this->getDiscountCustomer($user, $type, $date_start, $date_end, $in_bookkeeping);
387 if (is_array($tmp)) {
388 $data = array_merge($data, $tmp);
389 }
390 }
391 if (isModEnabled('supplier_invoice') && !getDolGlobalInt('ACCOUNTING_DISABLE_TRANSFER_ON_DISCOUNTS')) {
392 $tmp = $this->getDiscountSupplier($user, $type, $date_start, $date_end, $in_bookkeeping);
393 if (is_array($tmp)) {
394 $data = array_merge($data, $tmp);
395 }
396 }
397 break;
398 // case 2: // Sells Journal
399 // case 3: // Purchases Journal
400 // case 4: // Bank Journal
401 // case 5: // Expense reports Journal
402 // case 8: // Inventory Journal
403 // case 9: // hasnew Journal
404 }
405 }
406
407 return $data;
408 }
409
420 public function getAssetData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
421 {
422 global $conf, $langs;
423
424 if (!isModEnabled('asset')) {
425 return array();
426 }
427
428 require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
429 require_once DOL_DOCUMENT_ROOT . '/asset/class/asset.class.php';
430 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
431 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetdepreciationoptions.class.php';
432
433 $langs->loadLangs(array("assets"));
434
435 // Clean parameters
436 if (empty($type)) {
437 $type = 'view';
438 }
439 if (empty($in_bookkeeping)) {
440 $in_bookkeeping = 'notyet';
441 }
442
443 $sql = "";
444 $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";
445 $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";
446 $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";
447 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation as ad";
448 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "asset as a ON a.rowid = ad.fk_asset";
449 $sql .= " WHERE a.entity IN (" . getEntity('asset', 0) . ')'; // We don't share object for accountancy, we use source object sharing
450 $sql .= " AND a.status > 0";
451 if ($in_bookkeeping == 'already') {
452 $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')";
453 } elseif ($in_bookkeeping == 'notyet') {
454 $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')";
455 }
456 $sql .= " AND ad.ref != ''"; // not reversal lines
457 if ($date_start && $date_end) {
458 $sql .= " AND ad.depreciation_date >= '" . $this->db->idate($date_start) . "' AND ad.depreciation_date <= '" . $this->db->idate($date_end) . "'";
459 }
460 // Define begin binding date
461 if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
462 $sql .= " AND ad.depreciation_date >= '" . $this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING')) . "'";
463 }
464 $sql .= " ORDER BY ad.depreciation_date";
465
466 dol_syslog(__METHOD__, LOG_DEBUG);
467 $resql = $this->db->query($sql);
468 if (!$resql) {
469 $this->errors[] = $this->db->lasterror();
470 return -1;
471 }
472
473 $pre_data = array(
474 'elements' => array(),
475 );
476 while ($obj = $this->db->fetch_object($resql)) {
477 if (!isset($pre_data['elements'][$obj->rowid])) {
478 $pre_data['elements'][$obj->rowid] = array(
479 'ref' => $obj->asset_ref,
480 'label' => $obj->asset_label,
481 'acquisition_value_ht' => $obj->asset_acquisition_value_ht,
482 'depreciation' => array(),
483 );
484
485 // Disposal infos
486 if (isset($obj->asset_disposal_date)) {
487 $pre_data['elements'][$obj->rowid]['disposal'] = array(
488 'date' => $this->db->jdate($obj->asset_disposal_date),
489 'amount' => $obj->asset_disposal_amount_ht,
490 'subject_to_vat' => !empty($obj->asset_disposal_subject_to_vat),
491 );
492 }
493 }
494
495 $compta_debit = empty($obj->accountancy_code_debit) ? 'NotDefined' : $obj->accountancy_code_debit;
496 $compta_credit = empty($obj->accountancy_code_credit) ? 'NotDefined' : $obj->accountancy_code_credit;
497
498 $pre_data['elements'][$obj->rowid]['depreciation'][$obj->depreciation_id] = array(
499 'date' => $this->db->jdate($obj->depreciation_date),
500 'ref' => $obj->depreciation_ref,
501 'lines' => array(
502 $compta_debit => -$obj->depreciation_ht,
503 $compta_credit => $obj->depreciation_ht,
504 ),
505 );
506 }
507
508 $disposal_ref = $langs->transnoentitiesnoconv('AssetDisposal');
509 $journal = $this->code;
510 $journal_label = $this->label;
511 $journal_label_formatted = $langs->transnoentities($journal_label);
512 $now = dol_now();
513
514 $element_static = new Asset($this->db);
515
516 $journal_data = array();
517 foreach ($pre_data['elements'] as $pre_data_id => $pre_data_info) {
518 $element_static->id = $pre_data_id;
519 $element_static->ref = (string) $pre_data_info["ref"];
520 $element_static->label = (string) $pre_data_info["label"];
521 $element_static->acquisition_value_ht = $pre_data_info["acquisition_value_ht"];
522 $element_link = $element_static->getNomUrl(1, 'with_label');
523
524 $element_name_formatted_0 = dol_trunc($element_static->label, 16);
525 $label_operation = $element_static->getNomUrl(0, 'label', 16);
526
527 $element = array(
528 'ref' => dol_trunc($element_static->ref, 16, 'right', 'UTF-8', 1),
529 'error' => array_key_exists('error', $pre_data_info) ? $pre_data_info['error'] : '', // @phpstan-ignore-line
530 'blocks' => array(),
531 );
532
533 // Depreciation lines
534 //--------------------
535 foreach ($pre_data_info['depreciation'] as $depreciation_id => $line) {
536 $depreciation_ref = $line["ref"];
537 $depreciation_date = $line["date"];
538 $depreciation_date_formatted = dol_print_date($depreciation_date, 'day');
539
540 // lines
541 $blocks = array();
542 foreach ($line['lines'] as $account => $mt) {
543 $account_infos = $this->getAccountingAccountInfos($account);
544
545 if ($type == 'view') {
546 $account_to_show = length_accountg($account);
547 if (($account_to_show == "") || $account_to_show == 'NotDefined') {
548 $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
549 }
550
551 $blocks[] = array(
552 'date' => $depreciation_date_formatted,
553 'piece' => $element_link,
554 'account_accounting' => $account_to_show,
555 'subledger_account' => '',
556 'label_operation' => $label_operation . ' - ' . $depreciation_ref,
557 'debit' => $mt < 0 ? price(-$mt) : '',
558 'credit' => $mt >= 0 ? price($mt) : '',
559 );
560 } elseif ($type == 'bookkeeping') {
561 if ($account_infos['found']) {
562 $blocks[] = array(
563 'doc_date' => $depreciation_date,
564 'date_lim_reglement' => '',
565 'doc_ref' => $element_static->ref,
566 'date_creation' => $now,
567 'doc_type' => 'asset',
568 'fk_doc' => $element_static->id,
569 'fk_docdet' => $depreciation_id, // Useless, can be several lines that are source of this record to add
570 'thirdparty_code' => '',
571 'subledger_account' => '',
572 'subledger_label' => '',
573 'numero_compte' => $account,
574 'label_compte' => $account_infos['label'],
575 'label_operation' => $element_name_formatted_0 . ' - ' . $depreciation_ref,
576 'montant' => $mt,
577 'sens' => $mt < 0 ? 'D' : 'C',
578 'debit' => $mt < 0 ? -$mt : 0,
579 'credit' => $mt >= 0 ? $mt : 0,
580 'code_journal' => $journal,
581 'journal_label' => $journal_label_formatted,
582 'piece_num' => '',
583 'import_key' => '',
584 'fk_user_author' => $user->id,
585 'entity' => $conf->entity,
586 );
587 }
588 } else { // $type == 'csv'
589 $blocks[] = array(
590 $depreciation_date, // Date
591 $element_static->ref, // Piece
592 $account_infos['code_formatted_1'], // AccountAccounting
593 $element_name_formatted_0 . ' - ' . $depreciation_ref, // LabelOperation
594 $mt < 0 ? price(-$mt) : '', // Debit
595 $mt >= 0 ? price($mt) : '', // Credit
596 );
597 }
598 }
599 $element['blocks'][] = $blocks;
600 }
601
602 // Disposal line
603 //--------------------
604 if (!empty($pre_data_info['disposal'])) {
605 $disposal_date = $pre_data_info['disposal']['date'];
606
607 if ((!($date_start && $date_end) || ($date_start <= $disposal_date && $disposal_date <= $date_end)) &&
608 (!getDolGlobalString('ACCOUNTING_DATE_START_BINDING') || getDolGlobalInt('ACCOUNTING_DATE_START_BINDING') <= $disposal_date)
609 ) {
610 $disposal_amount = $pre_data_info['disposal']['amount'];
611 $disposal_subject_to_vat = $pre_data_info['disposal']['subject_to_vat'];
612 $disposal_date_formatted = dol_print_date($disposal_date, 'day');
613 $disposal_vat = getDolGlobalInt('ASSET_DISPOSAL_VAT') > 0 ? getDolGlobalInt('ASSET_DISPOSAL_VAT') : 20;
614
615 // Get accountancy codes
616 //---------------------------
617 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
618 $accountancy_codes = new AssetAccountancyCodes($this->db);
619 $result = $accountancy_codes->fetchAccountancyCodes($element_static->id);
620 if ($result < 0) {
621 $element['error'] = $accountancy_codes->errorsToString();
622 } else {
623 // Get last depreciation cumulative amount
624 $element_static->fetchDepreciationLines();
625 foreach ($element_static->depreciation_lines as $mode_key => $depreciation_lines) {
626 $accountancy_codes_list = $accountancy_codes->accountancy_codes[$mode_key];
627
628 if (!isset($accountancy_codes_list['value_asset_sold'])) {
629 continue;
630 }
631
632 $accountancy_code_value_asset_sold = empty($accountancy_codes_list['value_asset_sold']) ? 'NotDefined' : $accountancy_codes_list['value_asset_sold'];
633 $accountancy_code_depreciation_asset = empty($accountancy_codes_list['depreciation_asset']) ? 'NotDefined' : $accountancy_codes_list['depreciation_asset'];
634 $accountancy_code_asset = empty($accountancy_codes_list['asset']) ? 'NotDefined' : $accountancy_codes_list['asset'];
635 $accountancy_code_receivable_on_assignment = empty($accountancy_codes_list['receivable_on_assignment']) ? 'NotDefined' : $accountancy_codes_list['receivable_on_assignment'];
636 $accountancy_code_vat_collected = empty($accountancy_codes_list['vat_collected']) ? 'NotDefined' : $accountancy_codes_list['vat_collected'];
637 $accountancy_code_proceeds_from_sales = empty($accountancy_codes_list['proceeds_from_sales']) ? 'NotDefined' : $accountancy_codes_list['proceeds_from_sales'];
638
639 $last_cumulative_amount_ht = 0;
640 $depreciated_ids = array_keys($pre_data_info['depreciation']);
641 foreach ($depreciation_lines as $line) {
642 $last_cumulative_amount_ht = $line['cumulative_depreciation_ht'];
643 if (!in_array($line['id'], $depreciated_ids) && empty($line['bookkeeping']) && !empty($line['ref'])) {
644 break;
645 }
646 }
647
648 $lines = array();
649 $lines[0][$accountancy_code_value_asset_sold] = -((float) $element_static->acquisition_value_ht - $last_cumulative_amount_ht);
650 $lines[0][$accountancy_code_depreciation_asset] = - (float) $last_cumulative_amount_ht;
651 $lines[0][$accountancy_code_asset] = $element_static->acquisition_value_ht;
652
653 $disposal_amount_vat = $disposal_subject_to_vat ? (float) price2num($disposal_amount * $disposal_vat / 100, 'MT') : 0;
654 $lines[1][$accountancy_code_receivable_on_assignment] = -($disposal_amount + $disposal_amount_vat);
655 if ($disposal_subject_to_vat) {
656 $lines[1][$accountancy_code_vat_collected] = $disposal_amount_vat;
657 }
658 $lines[1][$accountancy_code_proceeds_from_sales] = $disposal_amount;
659
660 foreach ($lines as $lines_block) {
661 $blocks = array();
662 foreach ($lines_block as $account => $mt) {
663 $account_infos = $this->getAccountingAccountInfos($account);
664
665 if ($type == 'view') {
666 $account_to_show = length_accountg($account);
667 if (($account_to_show == "") || $account_to_show == 'NotDefined') {
668 $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
669 }
670
671 $blocks[] = array(
672 'date' => $disposal_date_formatted,
673 'piece' => $element_link,
674 'account_accounting' => $account_to_show,
675 'subledger_account' => '',
676 'label_operation' => $label_operation . ' - ' . $disposal_ref,
677 'debit' => $mt < 0 ? price(-$mt) : '',
678 'credit' => $mt >= 0 ? price($mt) : '',
679 );
680 } elseif ($type == 'bookkeeping') {
681 if ($account_infos['found']) {
682 $blocks[] = array(
683 'doc_date' => $disposal_date,
684 'date_lim_reglement' => '',
685 'doc_ref' => $element_static->ref,
686 'date_creation' => $now,
687 'doc_type' => 'asset',
688 'fk_doc' => $element_static->id,
689 'fk_docdet' => 0, // Useless, can be several lines that are source of this record to add
690 'thirdparty_code' => '',
691 'subledger_account' => '',
692 'subledger_label' => '',
693 'numero_compte' => $account,
694 'label_compte' => $account_infos['label'],
695 'label_operation' => $element_name_formatted_0 . ' - ' . $disposal_ref,
696 'montant' => $mt,
697 'sens' => $mt < 0 ? 'D' : 'C',
698 'debit' => $mt < 0 ? -$mt : 0,
699 'credit' => $mt >= 0 ? $mt : 0,
700 'code_journal' => $journal,
701 'journal_label' => $journal_label_formatted,
702 'piece_num' => '',
703 'import_key' => '',
704 'fk_user_author' => $user->id,
705 'entity' => $conf->entity,
706 );
707 }
708 } else { // $type == 'csv'
709 $blocks[] = array(
710 $disposal_date, // Date
711 $element_static->ref, // Piece
712 $account_infos['code_formatted_1'], // AccountAccounting
713 $element_name_formatted_0 . ' - ' . $disposal_ref, // LabelOperation
714 $mt < 0 ? price(-$mt) : '', // Debit
715 $mt >= 0 ? price($mt) : '', // Credit
716 );
717 }
718 }
719 $element['blocks'][] = $blocks;
720 }
721 }
722 }
723 }
724 }
725
726 $journal_data[(int) $pre_data_id] = $element;
727 }
728 unset($pre_data);
729
730 return $journal_data;
731 }
732
743 public function getDiscountCustomer(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
744 {
745 global $conf, $langs;
746
747 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
748 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
749 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
750
751 $langs->loadLangs(array('bills'));
752
753 // Clean parameters
754 if (empty($type)) {
755 $type = 'view';
756 }
757 if (empty($in_bookkeeping)) {
758 $in_bookkeeping = 'notyet';
759 }
760
761 // Build SQL - Customer invoices closed by discount
762 $sql = "SELECT f.rowid, f.ref, f.datef, f.date_closing, f.fk_soc, f.total_ttc";
763 $sql .= " FROM ".MAIN_DB_PREFIX."facture as f";
764 $sql .= " WHERE f.entity IN (".getEntity('invoice', 0).')'; // We don't share object for accountancy, we use source object sharing
765 $sql .= " AND f.fk_statut > 0";
766 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) { // Non common setup
767 $sql .= " AND f.type IN (".Facture::TYPE_STANDARD.",".Facture::TYPE_REPLACEMENT.",".Facture::TYPE_CREDIT_NOTE.",".Facture::TYPE_SITUATION.")";
768 } else {
769 $sql .= " AND f.type IN (".Facture::TYPE_STANDARD.",".Facture::TYPE_REPLACEMENT.",".Facture::TYPE_CREDIT_NOTE.",".Facture::TYPE_DEPOSIT.",".Facture::TYPE_SITUATION.")";
770 }
771 $sql .= " AND f.close_code = 'discount_vat'";
772 if ($date_start && $date_end) {
773 $sql .= " AND f.date_closing >= '".$this->db->idate($date_start)."' AND f.date_closing <= '".$this->db->idate($date_end)."'";
774 }
775 if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
776 $sql .= " AND f.date_closing >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
777 }
778 if ($in_bookkeeping == 'already') {
779 $sql .= " AND EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
780 $sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid";
781 $sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
782 } elseif ($in_bookkeeping == 'notyet') {
783 $sql .= " AND NOT EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
784 $sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid";
785 $sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
786 }
787 $sql .= " ORDER BY f.date_closing";
788
789 dol_syslog(__METHOD__, LOG_DEBUG);
790 $resql = $this->db->query($sql);
791 if (!$resql) {
792 $this->errors[] = $this->db->lasterror();
793 return -1;
794 }
795
796 $journal = $this->code;
797 $journal_label_formatted = $langs->transnoentities($this->label);
798 $now = dol_now();
799
800 $journal_data = array();
801 $invoice_static = new Facture($this->db);
802 $customer_static = new Societe($this->db);
803
804 // Accounting accounts
805 $acc_disc_granted = getDolGlobalString('ACCOUNTING_ACCOUNT_DISCOUNT_GRANTED');
806 $acc_vat_coll_def = getDolGlobalString('ACCOUNTING_VAT_BUY_ACCOUNT'); // Normal to apply vat default account for buy with customer's discount
807
808 while ($obj = $this->db->fetch_object($resql)) {
809 if ($invoice_static->fetch((int) $obj->rowid) <= 0) {
810 continue;
811 }
812
813 $customer_static->fetch($invoice_static->socid);
814 $account_customer_general = !empty($customer_static->accountancy_code_customer_general) ? $customer_static->accountancy_code_customer_general : getDolGlobalString('ACCOUNTING_ACCOUNT_CUSTOMER');
815 $account_customer_subsidiary = !empty($customer_static->code_compta_client) ? $customer_static->code_compta_client : '';
816
817 $piece_link = $invoice_static->getNomUrl(1, 'withlabel');
818
819 // Discounted amount including tax
820 $paid = (float) price2num($invoice_static->getSommePaiement(), 'MT');
821 $usedcn = (float) price2num($invoice_static->getSumCreditNotesUsed(), 'MT');
822 $useddep = (float) price2num($invoice_static->getSumDepositsUsed(), 'MT');
823 $ttc_inv = (float) price2num($invoice_static->total_ttc, 'MT');
824 $escompte_ttc = (float) price2num(max(0, $ttc_inv - $paid - $usedcn - $useddep), 'MT');
825 if ($escompte_ttc <= 0) {
826 continue;
827 }
828
829 $bookkeeping_static = new BookKeeping($this->db);
830 $thirdpartyname = (string) $customer_static->name;
831 $label_discount = $bookkeeping_static->accountingLabelForOperation($thirdpartyname, $invoice_static->ref, $langs->trans('DiscountGranted'));
832
833 // Distribution including VAT by rate
834 $ttcByRate = array();
835 $totalTTC = 0.0;
836 foreach ((array) $invoice_static->lines as $li) {
837 $ttc = (float) $li->total_ttc;
838 if (!$ttc) {
839 continue;
840 }
841 $key = number_format((float) $li->tva_tx, 3, '.', '');
842 if (!isset($ttcByRate[$key])) {
843 $ttcByRate[$key] = 0.0;
844 }
845 $ttcByRate[$key] += $ttc;
846 $totalTTC += $ttc;
847 }
848 if ($totalTTC <= 0) {
849 $ttcByRate = array("0.000" => $escompte_ttc);
850 $totalTTC = $escompte_ttc;
851 }
852
853 $element = array(
854 'ref' => dol_trunc($invoice_static->ref, 16, 'right', 'UTF-8', 1),
855 'error' => '',
856 'blocks' => array(),
857 );
858
859 $closingdate = !empty($obj->date_closing) ? $obj->date_closing : $obj->datef;
860
861 $docdate = $this->db->jdate($closingdate);
862 $docdate_fmt = dol_print_date($docdate, 'day');
863
864 $sumTTC = 0.0;
865 $i = 0;
866 $n = count($ttcByRate);
867 foreach ($ttcByRate as $rateStr => $ttcRateOnInvoice) {
868 $i++;
869 $rate = (float) $rateStr;
870
871 $ttc_part = (float) $escompte_ttc * ($ttcRateOnInvoice / $totalTTC);
872 if ($i == $n) {
873 $ttc_part = (float) price2num($escompte_ttc - $sumTTC, 'MT');
874 } else {
875 $ttc_part = (float) price2num($ttc_part, 'MT');
876 $sumTTC = (float) price2num($sumTTC + $ttc_part, 'MT');
877 }
878
879 if ($rate > 0) {
880 $ht_part = (float) price2num($ttc_part / (1 + $rate / 100), 'MT');
881 $tva_part = (float) price2num($ttc_part - $ht_part, 'MT');
882 } else {
883 $ht_part = $ttc_part;
884 $tva_part = 0.0;
885 }
886
887 // VAT deductible account (by rate if available)
888 // TODO write function to search the same vat code like the invoice
889 $acc_vat_coll = $acc_vat_coll_def;
890
891 $lines_view = array();
892 $lines_book = array();
893
894 // Discount granted
895 $acc_info_discountgranted = $this->getAccountingAccountInfos($acc_disc_granted);
896 if ($type == 'view') {
897 $lines_view[] = array(
898 'date' => $docdate_fmt,
899 'piece' => $piece_link,
900 'account_accounting' => length_accountg($acc_disc_granted),
901 'subledger_account' => '',
902 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('HT') . " (".$rateStr."%)",
903 'debit' => price($ht_part),
904 'credit' => '',
905 );
906 } elseif ($type == 'bookkeeping' && $acc_info_discountgranted['found']) {
907 $lines_book[] = array(
908 'doc_date' => $docdate,
909 'date_lim_reglement' => '',
910 'doc_ref' => $invoice_static->ref,
911 'date_creation' => $now,
912 'doc_type' => 'customer_invoice',
913 'fk_doc' => $invoice_static->id,
914 'fk_docdet' => 0,
915 'thirdparty_code' => $customer_static->code_client,
916 'subledger_account' => '',
917 'subledger_label' => '',
918 'numero_compte' => $acc_disc_granted,
919 'label_compte' => $acc_info_discountgranted['label'],
920 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('HT') . " (".$rateStr."%)",
921 'montant' => $ht_part,
922 'sens' => 'D',
923 'debit' => $ht_part,
924 'credit' => 0,
925 'code_journal' => $journal,
926 'journal_label' => $journal_label_formatted,
927 'piece_num' => 'OD-ESC-'.$invoice_static->ref,
928 'import_key' => '',
929 'fk_user_author' => $user->id,
930 'entity' => $conf->entity,
931 );
932 }
933
934 // VAT
935 if ($tva_part > 0) {
936 $acc_info_vatbuy = $this->getAccountingAccountInfos($acc_vat_coll);
937 if ($type == 'view') {
938 $lines_view[] = array(
939 'date' => $docdate_fmt,
940 'piece' => $piece_link,
941 'account_accounting' => length_accountg($acc_vat_coll),
942 'subledger_account' => '',
943 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)",
944 'debit' => price($tva_part),
945 'credit' => '',
946 );
947 } elseif ($type == 'bookkeeping' && $acc_info_vatbuy['found']) {
948 $lines_book[] = array(
949 'doc_date' => $docdate,
950 'date_lim_reglement' => '',
951 'doc_ref' => $invoice_static->ref,
952 'date_creation' => $now,
953 'doc_type' => 'customer_invoice',
954 'fk_doc' => $invoice_static->id,
955 'fk_docdet' => 0,
956 'thirdparty_code' => $customer_static->code_client,
957 'subledger_account' => '',
958 'subledger_label' => '',
959 'numero_compte' => $acc_vat_coll,
960 'label_compte' => $acc_info_vatbuy['label'],
961 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)",
962 'montant' => $tva_part,
963 'sens' => 'D',
964 'debit' => $tva_part,
965 'credit' => 0,
966 'code_journal' => $journal,
967 'journal_label' => $journal_label_formatted,
968 'piece_num' => 'OD-ESC-'.$invoice_static->ref,
969 'import_key' => '',
970 'fk_user_author' => $user->id,
971 'entity' => $conf->entity,
972 );
973 }
974 }
975
976 // Thirdparty
977 $acc_info_customeraccount = $this->getAccountingAccountInfos($account_customer_general);
978 if ($type == 'view') {
979 $lines_view[] = array(
980 'date' => $docdate_fmt,
981 'piece' => $piece_link,
982 'account_accounting' => length_accountg($account_customer_general),
983 'subledger_account' => length_accounta($account_customer_subsidiary),
984 'label_operation' => $label_discount.' - '.$langs->transnoentitiesnoconv('Customer'),
985 'debit' => '',
986 'credit' => price($ttc_part),
987 );
988 $element['blocks'][] = $lines_view;
989 } elseif ($type == 'bookkeeping' && $acc_info_customeraccount['found']) {
990 $lines_book[] = array(
991 'doc_date' => $docdate,
992 'date_lim_reglement' => '',
993 'doc_ref' => $invoice_static->ref,
994 'date_creation' => $now,
995 'doc_type' => 'customer_invoice',
996 'fk_doc' => $invoice_static->id,
997 'fk_docdet' => 0,
998 'thirdparty_code' => $customer_static->code_client,
999 'subledger_account' => $account_customer_subsidiary,
1000 'subledger_label' => $customer_static->name,
1001 'numero_compte' => $account_customer_general,
1002 'label_compte' => $acc_info_customeraccount['label'],
1003 'label_operation' => $label_discount.' - '.$langs->transnoentitiesnoconv('Customer'),
1004 'montant' => $ttc_part,
1005 'sens' => 'C',
1006 'debit' => 0,
1007 'credit' => $ttc_part,
1008 'code_journal' => $journal,
1009 'journal_label' => $journal_label_formatted,
1010 'piece_num' => 'OD-ESC-'.$invoice_static->ref,
1011 'import_key' => '',
1012 'fk_user_author' => $user->id,
1013 'entity' => $conf->entity,
1014 );
1015 $element['blocks'][] = $lines_book;
1016 } else { // CSV
1017 $element['blocks'][] = array(
1018 $docdate, // Date
1019 $invoice_static->ref, // Piece
1020 length_accountg($acc_disc_granted), // Account
1021 $label_discount." (".$rateStr."%)", // Label
1022 price($ht_part), // Debit
1023 '', // Credit
1024 );
1025 if ($tva_part > 0) {
1026 $element['blocks'][] = array(
1027 $docdate, $invoice_static->ref, length_accountg($acc_vat_coll), $label_discount." ". $langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)", price($tva_part), ''
1028 );
1029 }
1030 $element['blocks'][] = array(
1031 $docdate, $invoice_static->ref, length_accountg($account_customer_general), $label_discount.' - '.$langs->transnoentitiesnoconv('Customer'), '', price($ttc_part)
1032 );
1033 }
1034 }
1035
1036 $journal_data[(int) $invoice_static->id] = $element;
1037 }
1038
1039 return $journal_data;
1040 }
1041
1052 public function getDiscountSupplier(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
1053 {
1054 global $conf, $langs;
1055
1056 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
1057 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1058 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
1059
1060 $langs->loadLangs(array('suppliers'));
1061
1062 // Clean parameters
1063 if (empty($type)) {
1064 $type = 'view';
1065 }
1066 if (empty($in_bookkeeping)) {
1067 $in_bookkeeping = 'notyet';
1068 }
1069
1070 // SQL - Supplier invoices closed by discount
1071 $sql = "SELECT ff.rowid, ff.ref, ff.datef, ff.date_closing, ff.fk_soc, ff.total_ttc";
1072 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as ff";
1073 $sql .= " WHERE ff.entity IN (".getEntity('facture_fourn', 0).")"; // We don't share object for accountancy
1074 $sql .= " AND ff.fk_statut > 0";
1075 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
1076 $sql .= " AND ff.type IN (".FactureFournisseur::TYPE_STANDARD.",".FactureFournisseur::TYPE_REPLACEMENT.",".FactureFournisseur::TYPE_CREDIT_NOTE.",".FactureFournisseur::TYPE_SITUATION.")";
1077 } else {
1078 $sql .= " AND ff.type IN (".FactureFournisseur::TYPE_STANDARD.",".FactureFournisseur::TYPE_REPLACEMENT.",".FactureFournisseur::TYPE_CREDIT_NOTE.",".FactureFournisseur::TYPE_DEPOSIT.",".FactureFournisseur::TYPE_SITUATION.")";
1079 }
1080 $sql .= " AND ff.close_code = 'discount_vat'";
1081 if ($date_start && $date_end) {
1082 $sql .= " AND ff.date_closing >= '".$this->db->idate($date_start)."' AND ff.date_closing <= '".$this->db->idate($date_end)."'";
1083 }
1084 if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
1085 $sql .= " AND ff.date_closing >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
1086 }
1087 if ($in_bookkeeping == 'already') {
1088 $sql .= " AND EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
1089 $sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid";
1090 $sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
1091 } elseif ($in_bookkeeping == 'notyet') {
1092 $sql .= " AND NOT EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
1093 $sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid";
1094 $sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
1095 }
1096 $sql .= " ORDER BY ff.date_closing";
1097
1098 dol_syslog(__METHOD__, LOG_DEBUG);
1099 $resql = $this->db->query($sql);
1100 if (!$resql) {
1101 $this->errors[] = $this->db->lasterror();
1102 return -1;
1103 }
1104
1105 $journal = $this->code;
1106 $journal_label_formatted = $langs->transnoentities($this->label);
1107 $now = dol_now();
1108
1109 $journal_data = array();
1110 $invoicesupplier_static = new FactureFournisseur($this->db);
1111 $supplier_static = new Societe($this->db);
1112
1113 // Accounting accounts
1114 $acc_disc_recv = getDolGlobalString('ACCOUNTING_ACCOUNT_DISCOUNT_RECEIVED');
1115 $acc_vat_ded_def = getDolGlobalString('ACCOUNTING_VAT_SOLD_ACCOUNT'); // Normal to apply vat default account for sold with supplier's discount
1116
1117 while ($obj = $this->db->fetch_object($resql)) {
1118 if ($invoicesupplier_static->fetch((int) $obj->rowid) <= 0) {
1119 continue;
1120 }
1121
1122 $supplier_static->fetch($invoicesupplier_static->socid);
1123 $account_supplier_general = !empty($supplier_static->accountancy_code_supplier_general) ? $supplier_static->accountancy_code_supplier_general : getDolGlobalString('ACCOUNTING_ACCOUNT_SUPPLIER');
1124 $account_supplier_subsidiary = !empty($supplier_static->code_compta_fournisseur) ? $supplier_static->code_compta_fournisseur : '';
1125
1126 $piece_link = $invoicesupplier_static->getNomUrl(1, 'withlabel');
1127
1128 // Discounted amount including tax
1129 $paid = (float) price2num($invoicesupplier_static->getSommePaiement(), 'MT');
1130 $usedcn = (float) price2num($invoicesupplier_static->getSumCreditNotesUsed(), 'MT');
1131 $useddep = (float) price2num($invoicesupplier_static->getSumDepositsUsed(), 'MT');
1132 $ttc_inv = (float) price2num($invoicesupplier_static->total_ttc, 'MT');
1133 $escompte_ttc = (float) price2num(max(0, $ttc_inv - $paid - $usedcn - $useddep), 'MT');
1134 if ($escompte_ttc <= 0) {
1135 continue;
1136 }
1137
1138 $bookkeeping_static = new BookKeeping($this->db);
1139 $thirdpartyname = (string) $supplier_static->name;
1140 $label_discount = $bookkeeping_static->accountingLabelForOperation($thirdpartyname, $invoicesupplier_static->ref, $langs->trans('DiscountReceived'));
1141
1142 // Distribution including VAT by rate
1143 $ttcByRate = array();
1144 $totalTTC = 0.0;
1145 foreach ((array) $invoicesupplier_static->lines as $li) {
1146 $ttc = (float) $li->total_ttc;
1147 if (!$ttc) {
1148 continue;
1149 }
1150 $key = number_format((float) $li->tva_tx, 3, '.', '');
1151 if (!isset($ttcByRate[$key])) {
1152 $ttcByRate[$key] = 0.0;
1153 }
1154 $ttcByRate[$key] += $ttc;
1155 $totalTTC += $ttc;
1156 }
1157 if ($totalTTC <= 0) {
1158 $ttcByRate = array("0.000" => $escompte_ttc);
1159 $totalTTC = $escompte_ttc;
1160 }
1161
1162 $element = array(
1163 'ref' => dol_trunc($invoicesupplier_static->ref, 16, 'right', 'UTF-8', 1),
1164 'error' => '',
1165 'blocks' => array(),
1166 );
1167
1168 $closingdate = !empty($obj->date_closing) ? $obj->date_closing : $obj->datef;
1169
1170 $docdate = $this->db->jdate($closingdate);
1171 $docdate_fmt = dol_print_date($docdate, 'day');
1172
1173 $sumTTC = 0.0;
1174 $i = 0;
1175 $n = count($ttcByRate);
1176 foreach ($ttcByRate as $rateStr => $ttcRateOnInvoice) {
1177 $i++;
1178 $rate = (float) $rateStr;
1179
1180 $ttc_part = $escompte_ttc * ($ttcRateOnInvoice / $totalTTC);
1181 if ($i == $n) {
1182 $ttc_part = (float) price2num($escompte_ttc - $sumTTC, 'MT');
1183 } else {
1184 $ttc_part = (float) price2num($ttc_part, 'MT');
1185 $sumTTC = (float) price2num($sumTTC + $ttc_part, 'MT');
1186 }
1187
1188 if ($rate > 0) {
1189 $ht_part = (float) price2num($ttc_part / (1 + $rate / 100), 'MT');
1190 $tva_part = (float) price2num($ttc_part - $ht_part, 'MT');
1191 } else {
1192 $ht_part = $ttc_part;
1193 $tva_part = 0.0;
1194 }
1195
1196 // VAT collected account (by rate if available)
1197 // TODO write function to search the same vat code like the supplier invoice
1198 $acc_vat_ded = $acc_vat_ded_def;
1199
1200 $lines_view = array();
1201 $lines_book = array();
1202
1203 // Thirdparty
1204 $acc_info_supplieraccount = $this->getAccountingAccountInfos($account_supplier_general);
1205 if ($type == 'view') {
1206 $lines_view[] = array(
1207 'date' => $docdate_fmt,
1208 'piece' => $piece_link,
1209 'account_accounting' => length_accountg($account_supplier_general),
1210 'subledger_account' => length_accounta($account_supplier_subsidiary),
1211 'label_operation' => $label_discount.' - '.$langs->transnoentitiesnoconv('Supplier'),
1212 'debit' => price($ttc_part),
1213 'credit' => '',
1214 );
1215 } elseif ($type == 'bookkeeping' && $acc_info_supplieraccount['found']) {
1216 $lines_book[] = array(
1217 'doc_date' => $docdate,
1218 'date_lim_reglement' => '',
1219 'doc_ref' => $invoicesupplier_static->ref,
1220 'date_creation' => $now,
1221 'doc_type' => 'supplier_invoice',
1222 'fk_doc' => $invoicesupplier_static->id,
1223 'fk_docdet' => 0,
1224 'thirdparty_code' => $supplier_static->code_fournisseur,
1225 'subledger_account' => $account_supplier_subsidiary,
1226 'subledger_label' => $supplier_static->name,
1227 'numero_compte' => $account_supplier_general,
1228 'label_compte' => $acc_info_supplieraccount['label'],
1229 'label_operation' => $label_discount.' - '.$langs->transnoentitiesnoconv('Supplier'),
1230 'montant' => $ttc_part,
1231 'sens' => 'D',
1232 'debit' => $ttc_part,
1233 'credit' => 0,
1234 'code_journal' => $journal,
1235 'journal_label' => $journal_label_formatted,
1236 'piece_num' => 'OD-ESC-FRS-'.$invoicesupplier_static->ref,
1237 'import_key' => '',
1238 'fk_user_author' => $user->id,
1239 'entity' => $conf->entity,
1240 );
1241 }
1242
1243 // Discount received
1244 $acc_info_discountreceived = $this->getAccountingAccountInfos($acc_disc_recv);
1245 if ($type == 'view') {
1246 $lines_view[] = array(
1247 'date' => $docdate_fmt,
1248 'piece' => $piece_link,
1249 'account_accounting' => length_accountg($acc_disc_recv),
1250 'subledger_account' => '',
1251 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('HT') . " (".$rateStr."%)",
1252 'debit' => '',
1253 'credit' => price($ht_part),
1254 );
1255 } elseif ($type == 'bookkeeping' && $acc_info_discountreceived['found']) {
1256 $lines_book[] = array(
1257 'doc_date' => $docdate,
1258 'date_lim_reglement' => '',
1259 'doc_ref' => $invoicesupplier_static->ref,
1260 'date_creation' => $now,
1261 'doc_type' => 'supplier_invoice',
1262 'fk_doc' => $invoicesupplier_static->id,
1263 'fk_docdet' => 0,
1264 'thirdparty_code' => $supplier_static->code_fournisseur,
1265 'subledger_account' => '',
1266 'subledger_label' => '',
1267 'numero_compte' => $acc_disc_recv,
1268 'label_compte' => $acc_info_discountreceived['label'],
1269 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('HT') . " (".$rateStr."%)",
1270 'montant' => $ht_part,
1271 'sens' => 'C',
1272 'debit' => 0,
1273 'credit' => $ht_part,
1274 'code_journal' => $journal,
1275 'journal_label' => $journal_label_formatted,
1276 'piece_num' => 'OD-ESC-FRS-'.$invoicesupplier_static->ref,
1277 'import_key' => '',
1278 'fk_user_author' => $user->id,
1279 'entity' => $conf->entity,
1280 );
1281 }
1282
1283 // VAT
1284 if ($tva_part > 0) {
1285 $acc_info_vatbuy = $this->getAccountingAccountInfos($acc_vat_ded);
1286 if ($type == 'view') {
1287 $lines_view[] = array(
1288 'date' => $docdate_fmt,
1289 'piece' => $piece_link,
1290 'account_accounting' => length_accountg($acc_vat_ded),
1291 'subledger_account' => '',
1292 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)",
1293 'debit' => '',
1294 'credit' => price($tva_part),
1295 );
1296 $element['blocks'][] = $lines_view;
1297 } elseif ($type == 'bookkeeping' && $acc_info_vatbuy['found']) {
1298 $lines_book[] = array(
1299 'doc_date' => $docdate,
1300 'date_lim_reglement' => '',
1301 'doc_ref' => $invoicesupplier_static->ref,
1302 'date_creation' => $now,
1303 'doc_type' => 'supplier_invoice',
1304 'fk_doc' => $invoicesupplier_static->id,
1305 'fk_docdet' => 0,
1306 'thirdparty_code' => $supplier_static->code_fournisseur,
1307 'subledger_account' => '',
1308 'subledger_label' => '',
1309 'numero_compte' => $acc_vat_ded,
1310 'label_compte' => $acc_info_vatbuy['label'],
1311 'label_operation' => $label_discount." - " .$langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)",
1312 'montant' => $tva_part,
1313 'sens' => 'C',
1314 'debit' => 0,
1315 'credit' => $tva_part,
1316 'code_journal' => $journal,
1317 'journal_label' => $journal_label_formatted,
1318 'piece_num' => 'OD-ESC-FRS-'.$invoicesupplier_static->ref,
1319 'import_key' => '',
1320 'fk_user_author' => $user->id,
1321 'entity' => $conf->entity,
1322 );
1323 $element['blocks'][] = $lines_book;
1324 }
1325 } else {
1326 // si TVA = 0, pousser les 2 lignes view/bookkeeping déjà constituées
1327 if ($type == 'view') {
1328 $element['blocks'][] = $lines_view;
1329 } elseif ($type == 'bookkeeping') {
1330 $element['blocks'][] = $lines_book;
1331 } else { // csv
1332 $element['blocks'][] = array($docdate, $invoicesupplier_static->ref, length_accountg($account_supplier_general), $label_discount.' - '.$langs->transnoentitiesnoconv('Supplier'), price($ttc_part), '');
1333 $element['blocks'][] = array($docdate, $invoicesupplier_static->ref, length_accountg($acc_disc_recv), $label_discount.' ('.$rateStr.'%)', '', price($ht_part));
1334 }
1335 }
1336
1337 // CSV
1338 if ($type == 'csv') {
1339 $element['blocks'][] = array(
1340 $docdate, $invoicesupplier_static->ref, length_accountg($acc_vat_ded), $label_discount." ". $langs->transnoentitiesnoconv('VAT') . " (".$rateStr."%)", '', $tva_part > 0 ? price($tva_part) : ''
1341 );
1342 }
1343 }
1344
1345 $journal_data[(int) $invoicesupplier_static->id] = $element;
1346 }
1347
1348 return $journal_data;
1349 }
1350
1394 public function writeIntoBookkeeping(User $user, &$journal_data = array(), $max_nb_errors = 10)
1395 {
1396 global $conf, $langs, $hookmanager;
1397 require_once DOL_DOCUMENT_ROOT . '/accountancy/class/bookkeeping.class.php';
1398
1399 $error = 0;
1400
1401 $hookmanager->initHooks(array('accountingjournaldao'));
1402 $parameters = array('journal_data' => &$journal_data);
1403 $reshook = $hookmanager->executeHooks('writeBookkeeping', $parameters, $this); // Note that $action and $object may have been
1404 if ($reshook < 0) {
1405 $this->error = $hookmanager->error;
1406 $this->errors = $hookmanager->errors;
1407 return -1;
1408 } elseif (empty($reshook)) {
1409 // Clean parameters
1410 if (!is_array($journal_data)) {
1411 $journal_data = array();
1412 }
1413
1414 foreach ($journal_data as $element_id => $element) {
1415 $error_for_line = 0;
1416 $total_credit = 0;
1417 $total_debit = 0;
1418
1419 $this->db->begin();
1420
1421 if ($element['error'] == 'somelinesarenotbound') {
1422 $error++;
1423 $error_for_line++;
1424 $this->errors[] = $langs->trans('ErrorInvoiceContainsLinesNotYetBounded', $element['ref']);
1425 }
1426
1427 if (!$error_for_line) {
1428 foreach ($element['blocks'] as $lines) {
1429 foreach ($lines as $line) {
1430 $bookkeeping = new BookKeeping($this->db);
1431 $bookkeeping->doc_date = (int) $line['doc_date'];
1432 $bookkeeping->date_lim_reglement = (int) $line['date_lim_reglement'];
1433 $bookkeeping->doc_ref = $line['doc_ref'];
1434 $bookkeeping->date_creation = $line['date_creation']; // not used
1435 $bookkeeping->doc_type = $line['doc_type'];
1436 $bookkeeping->fk_doc = $line['fk_doc'];
1437 $bookkeeping->fk_docdet = $line['fk_docdet'];
1438 $bookkeeping->thirdparty_code = $line['thirdparty_code'];
1439 $bookkeeping->subledger_account = $line['subledger_account'];
1440 $bookkeeping->subledger_label = $line['subledger_label'];
1441 $bookkeeping->numero_compte = $line['numero_compte'];
1442 $bookkeeping->label_compte = $line['label_compte'];
1443 $bookkeeping->label_operation = $line['label_operation'];
1444 $bookkeeping->montant = $line['montant']; // Deprecated: sens/debit/credit (and deprecated amount...)
1445 $bookkeeping->sens = $line['sens'];
1446 $bookkeeping->debit = (float) $line['debit'];
1447 $bookkeeping->credit = (float) $line['credit'];
1448 $bookkeeping->code_journal = $line['code_journal'];
1449 $bookkeeping->journal_label = $line['journal_label'];
1450 $bookkeeping->piece_num = (int) $line['piece_num'];
1451 $bookkeeping->import_key = $line['import_key'];
1452 $bookkeeping->fk_user_author = $user->id;
1453 $bookkeeping->entity = $conf->entity;
1454
1455 $total_debit += $bookkeeping->debit;
1456 $total_credit += $bookkeeping->credit;
1457
1458 $result = $bookkeeping->create($user);
1459 if ($result < 0) {
1460 if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists') { // Already exists
1461 $error++;
1462 $error_for_line++;
1463 $journal_data[$element_id]['error'] = 'alreadyjournalized';
1464 } else {
1465 $error++;
1466 $error_for_line++;
1467 $journal_data[$element_id]['error'] = 'other';
1468 $this->errors[] = $bookkeeping->errorsToString();
1469 }
1470 }
1471 //
1472 // if (!$error_for_line && isModEnabled('asset') && $this->nature == 1 && $bookkeeping->fk_doc > 0) {
1473 // // Set last cumulative depreciation
1474 // require_once DOL_DOCUMENT_ROOT . '/asset/class/asset.class.php';
1475 // $asset = new Asset($this->db);
1476 // $result = $asset->setLastCumulativeDepreciation($bookkeeping->fk_doc);
1477 // if ($result < 0) {
1478 // $error++;
1479 // $error_for_line++;
1480 // $journal_data[$element_id]['error'] = 'other';
1481 // $this->errors[] = $asset->errorsToString();
1482 // }
1483 // }
1484 }
1485
1486 if ($error_for_line) {
1487 break;
1488 }
1489 }
1490 }
1491
1492 // Protection against a bug on lines before
1493 if (!$error_for_line && (price2num($total_debit, 'MT') != price2num($total_credit, 'MT'))) {
1494 $error++;
1495 $error_for_line++;
1496 $journal_data[$element_id]['error'] = 'amountsnotbalanced';
1497 $this->errors[] = 'Try to insert a non balanced transaction in book for ' . json_encode($element['blocks']) . '. Canceled. Surely a bug.';
1498 }
1499
1500 if (!$error_for_line) {
1501 $this->db->commit();
1502 } else {
1503 $this->db->rollback();
1504
1505 if ($error >= $max_nb_errors) {
1506 $this->errors[] = $langs->trans("ErrorTooManyErrorsProcessStopped");
1507 break; // Break in the foreach
1508 }
1509 }
1510 }
1511 }
1512
1513 return $error ? -$error : 1;
1514 }
1515
1537 public function exportCsv(&$journal_data = array(), $search_date_end = 0, $sep = '')
1538 {
1539 global $conf, $langs, $hookmanager;
1540
1541 if (empty($sep)) {
1542 $sep = getDolGlobalString('ACCOUNTING_EXPORT_SEPARATORCSV');
1543 }
1544 $out = '';
1545
1546 // Hook
1547 $hookmanager->initHooks(array('accountingjournaldao'));
1548 $parameters = array('journal_data' => &$journal_data, 'search_date_end' => &$search_date_end, 'sep' => &$sep, 'out' => &$out);
1549 $reshook = $hookmanager->executeHooks('exportCsv', $parameters, $this); // Note that $action and $object may have been
1550 if ($reshook < 0) {
1551 $this->error = $hookmanager->error;
1552 $this->errors = $hookmanager->errors;
1553 return -1;
1554 } elseif (empty($reshook)) {
1555 // Clean parameters
1556 $journal_data = is_array($journal_data) ? $journal_data : array();
1557
1558 // CSV header line
1559 $header = array();
1560 if ($this->nature == 4) {
1561 $header = array(
1562 $langs->transnoentitiesnoconv("BankId"),
1563 $langs->transnoentitiesnoconv("Date"),
1564 $langs->transnoentitiesnoconv("PaymentMode"),
1565 $langs->transnoentitiesnoconv("AccountAccounting"),
1566 $langs->transnoentitiesnoconv("LedgerAccount"),
1567 $langs->transnoentitiesnoconv("SubledgerAccount"),
1568 $langs->transnoentitiesnoconv("Label"),
1569 $langs->transnoentitiesnoconv("AccountingDebit"),
1570 $langs->transnoentitiesnoconv("AccountingCredit"),
1571 $langs->transnoentitiesnoconv("Journal"),
1572 $langs->transnoentitiesnoconv("Note"),
1573 );
1574 } elseif ($this->nature == 5) {
1575 $header = array(
1576 $langs->transnoentitiesnoconv("Date"),
1577 $langs->transnoentitiesnoconv("Piece"),
1578 $langs->transnoentitiesnoconv("AccountAccounting"),
1579 $langs->transnoentitiesnoconv("LabelOperation"),
1580 $langs->transnoentitiesnoconv("AccountingDebit"),
1581 $langs->transnoentitiesnoconv("AccountingCredit"),
1582 );
1583 } elseif ($this->nature == 1) {
1584 $header = array(
1585 $langs->transnoentitiesnoconv("Date"),
1586 $langs->transnoentitiesnoconv("Piece"),
1587 $langs->transnoentitiesnoconv("AccountAccounting"),
1588 $langs->transnoentitiesnoconv("LabelOperation"),
1589 $langs->transnoentitiesnoconv("AccountingDebit"),
1590 $langs->transnoentitiesnoconv("AccountingCredit"),
1591 );
1592 }
1593
1594 if (!empty($header)) {
1595 $out .= '"' . implode('"' . $sep . '"', $header) . '"' . "\n";
1596 }
1597 foreach ($journal_data as $element_id => $element) {
1598 foreach ($element['blocks'] as $lines) {
1599 foreach ($lines as $line) {
1600 $out .= '"' . implode('"' . $sep . '"', $line) . '"' . "\n";
1601 }
1602 }
1603 }
1604 }
1605
1606 return $out;
1607 }
1608
1615 public function getAccountingAccountInfos($account)
1616 {
1617 if (!isset(self::$accounting_account_cached[$account])) {
1618 require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
1619 require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountingaccount.class.php';
1620 $accountingaccount = new AccountingAccount($this->db);
1621 $result = $accountingaccount->fetch(0, $account, true);
1622 if ($result > 0) {
1623 self::$accounting_account_cached[$account] = array(
1624 'found' => true,
1625 'label' => $accountingaccount->label,
1626 'code_formatted_1' => length_accountg(html_entity_decode($account)),
1627 'label_formatted_1' => mb_convert_encoding(dol_trunc($accountingaccount->label, 32), 'ISO-8859-1'),
1628 'label_formatted_2' => dol_trunc($accountingaccount->label, 32),
1629 );
1630 } else {
1631 self::$accounting_account_cached[$account] = array(
1632 'found' => false,
1633 'label' => '',
1634 'code_formatted_1' => length_accountg(html_entity_decode($account)),
1635 'label_formatted_1' => '',
1636 'label_formatted_2' => '',
1637 );
1638 }
1639 }
1640
1641 return self::$accounting_account_cached[$account];
1642 }
1643}
length_accountg($account)
Return General accounting account with defined length (used for product and miscellaneous)
length_accounta($accounta)
Return Auxiliary accounting account of thirdparties with defined length.
$object ref
Definition info.php:90
Class to manage accounting accounts.
Class to manage accounting journals.
writeIntoBookkeeping(User $user, &$journal_data=array(), $max_nb_errors=10)
Write bookkeeping.
getData(User $user, $type='view', $date_start=null, $date_end=null, $in_bookkeeping='notyet')
Get journal data.
exportCsv(&$journal_data=array(), $search_date_end=0, $sep='')
Export journal CSV ISO and not UTF8 !
getDiscountSupplier(User $user, $type='view', $date_start=null, $date_end=null, $in_bookkeeping='notyet')
Get supplier discount (escompte) data for various journal.
getNomUrl($withpicto=0, $withlabel=0, $nourl=0, $moretitle='', $notooltip=0)
Return clickable name (with picto eventually)
fetch($rowid=0, $journal_code=null)
Load an object from database.
getDiscountCustomer(User $user, $type='view', $date_start=null, $date_end=null, $in_bookkeeping='notyet')
Get customer discount (escompte) data for various journal.
LibType($nature, $mode=0)
Return type of an accounting journal.
getAccountingAccountInfos($account)
Get accounting account info.
create($user)
Create a new Accounting Journal.
getAssetData(User $user, $type='view', $date_start=null, $date_end=null, $in_bookkeeping='notyet')
Get asset data for various journal.
getLibType($mode=0)
Return the label of the status.
Class for AssetAccountancyCodes.
Class for Asset.
Class to manage Ledger (General Ledger and Subledger)
const TYPE_SITUATION
Situation invoice.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class to manage suppliers invoices.
const TYPE_DEPOSIT
Deposit invoice.
const TYPE_CREDIT_NOTE
Credit note invoice.
const TYPE_REPLACEMENT
Replacement invoice.
Class to manage invoices.
const TYPE_REPLACEMENT
Replacement invoice.
const TYPE_SITUATION
Situation invoice.
const TYPE_DEPOSIT
Deposit invoice.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
dol_now($mode='gmt')
Return date for now.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
editval_textarea active