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