dolibarr  17.0.4
accountingjournal.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2017-2022 OpenDSI <support@open-dsi.fr>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16  */
17 
28 {
32  public $element = 'accounting_journal';
33 
37  public $table_element = 'accounting_journal';
38 
42  public $fk_element = '';
43 
47  public $ismultientitymanaged = 0;
48 
52  public $picto = 'generic';
53 
57  public $rowid;
58 
62  public $code;
63 
67  public $label;
68 
72  public $nature;
73 
77  public $active;
78 
82  public $lines;
83 
87  static public $accounting_account_cached = array();
88 
92  static public $nature_maps = array(
93  1 => 'variousoperations',
94  2 => 'sells',
95  3 => 'purchases',
96  4 => 'bank',
97  5 => 'expensereports',
98  8 => 'inventories',
99  9 => 'hasnew',
100  );
101 
107  public function __construct($db)
108  {
109  $this->db = $db;
110  }
111 
119  public function fetch($rowid = null, $journal_code = null)
120  {
121  global $conf;
122 
123  if ($rowid || $journal_code) {
124  $sql = "SELECT rowid, code, label, nature, active";
125  $sql .= " FROM ".MAIN_DB_PREFIX."accounting_journal";
126  $sql .= " WHERE";
127  if ($rowid) {
128  $sql .= " rowid = ".((int) $rowid);
129  } elseif ($journal_code) {
130  $sql .= " code = '".$this->db->escape($journal_code)."'";
131  $sql .= " AND entity = ".$conf->entity;
132  }
133 
134  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
135  $result = $this->db->query($sql);
136  if ($result) {
137  $obj = $this->db->fetch_object($result);
138 
139  if ($obj) {
140  $this->id = $obj->rowid;
141  $this->rowid = $obj->rowid;
142 
143  $this->code = $obj->code;
144  $this->ref = $obj->code;
145  $this->label = $obj->label;
146  $this->nature = $obj->nature;
147  $this->active = $obj->active;
148 
149  return $this->id;
150  } else {
151  return 0;
152  }
153  } else {
154  $this->error = "Error ".$this->db->lasterror();
155  $this->errors[] = "Error ".$this->db->lasterror();
156  }
157  }
158  return -1;
159  }
160 
173  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
174  {
175  $sql = "SELECT rowid, code, label, nature, active";
176  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
177  // Manage filter
178  $sqlwhere = array();
179  if (count($filter) > 0) {
180  foreach ($filter as $key => $value) {
181  if ($key == 't.code' || $key == 't.label' || $key == 't.nature') {
182  $sqlwhere[] = $key.'\''.$this->db->escape($value).'\'';
183  } elseif ($key == 't.rowid' || $key == 't.active') {
184  $sqlwhere[] = $key.'='.$value;
185  }
186  }
187  }
188  $sql .= ' WHERE 1 = 1';
189  $sql .= " AND entity IN (".getEntity('accountancy').")";
190  if (count($sqlwhere) > 0) {
191  $sql .= " AND ".implode(" ".$filtermode." ", $sqlwhere);
192  }
193 
194  if (!empty($sortfield)) {
195  $sql .= $this->db->order($sortfield, $sortorder);
196  }
197  if (!empty($limit)) {
198  $sql .= $this->db->plimit($limit + 1, $offset);
199  }
200  $this->lines = array();
201 
202  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
203  $resql = $this->db->query($sql);
204  if ($resql) {
205  $num = $this->db->num_rows($resql);
206 
207  while ($obj = $this->db->fetch_object($resql)) {
208  $line = new self($this->db);
209 
210  $line->id = $obj->rowid;
211  $line->code = $obj->code;
212  $line->label = $obj->label;
213  $line->nature = $obj->nature;
214  $line->active = $obj->active;
215 
216  $this->lines[] = $line;
217  }
218 
219  $this->db->free($resql);
220 
221  return $num;
222  } else {
223  $this->errors[] = 'Error '.$this->db->lasterror();
224  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
225 
226  return -1;
227  }
228  }
229 
240  public function getNomUrl($withpicto = 0, $withlabel = 0, $nourl = 0, $moretitle = '', $notooltip = 0)
241  {
242  global $langs, $conf, $user, $hookmanager;
243 
244  if (!empty($conf->dol_no_mouse_hover)) {
245  $notooltip = 1; // Force disable tooltips
246  }
247 
248  $result = '';
249 
250  $url = DOL_URL_ROOT.'/accountancy/admin/journals_list.php?id=35';
251 
252  $label = '<u>'.$langs->trans("ShowAccountingJournal").'</u>';
253  if (!empty($this->code)) {
254  $label .= '<br><b>'.$langs->trans('Code').':</b> '.$this->code;
255  }
256  if (!empty($this->label)) {
257  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$langs->transnoentities($this->label);
258  }
259  if ($moretitle) {
260  $label .= ' - '.$moretitle;
261  }
262 
263  $linkclose = '';
264  if (empty($notooltip)) {
265  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
266  $label = $langs->trans("ShowAccountingJournal");
267  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
268  }
269  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
270  $linkclose .= ' class="classfortooltip"';
271  }
272 
273  $linkstart = '<a href="'.$url.'"';
274  $linkstart .= $linkclose.'>';
275  $linkend = '</a>';
276 
277  if ($nourl) {
278  $linkstart = '';
279  $linkclose = '';
280  $linkend = '';
281  }
282 
283  $label_link = $this->code;
284  if ($withlabel != 2 && !empty($this->label)) {
285  $label_link .= ' - '.($nourl ? '<span class="opacitymedium">' : '').$langs->transnoentities($this->label).($nourl ? '</span>' : '');
286  }
287  if ($withlabel == 2 && !empty($this->nature)) {
288  $key = $langs->trans("AccountingJournalType".strtoupper($this->nature));
289  $transferlabel = ($this->nature && $key != "AccountingJournalType".strtoupper($langs->trans($this->nature)) ? $key : $this->label);
290  $label_link .= ' - '.($nourl ? '<span class="opacitymedium">' : '').$transferlabel.($nourl ? '</span>' : '');
291  }
292 
293  $result .= $linkstart;
294  if ($withpicto) {
295  $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);
296  }
297  if ($withpicto != 2) {
298  $result .= $label_link;
299  }
300  $result .= $linkend;
301 
302  global $action;
303  $hookmanager->initHooks(array('accountingjournaldao'));
304  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
305  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
306  if ($reshook > 0) {
307  $result = $hookmanager->resPrint;
308  } else {
309  $result .= $hookmanager->resPrint;
310  }
311  return $result;
312  }
313 
320  public function getLibType($mode = 0)
321  {
322  return $this->LibType($this->nature, $mode);
323  }
324 
325  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
333  public function LibType($nature, $mode = 0)
334  {
335  // phpcs:enable
336  global $langs;
337 
338  $langs->loadLangs(array("accountancy"));
339 
340  if ($mode == 0) {
341  $prefix = '';
342  if ($nature == 9) {
343  return $langs->trans('AccountingJournalType9');
344  } elseif ($nature == 5) {
345  return $langs->trans('AccountingJournalType5');
346  } elseif ($nature == 4) {
347  return $langs->trans('AccountingJournalType4');
348  } elseif ($nature == 3) {
349  return $langs->trans('AccountingJournalType3');
350  } elseif ($nature == 2) {
351  return $langs->trans('AccountingJournalType2');
352  } elseif ($nature == 1) {
353  return $langs->trans('AccountingJournalType1');
354  }
355  } elseif ($mode == 1) {
356  if ($nature == 9) {
357  return $langs->trans('AccountingJournalType9');
358  } elseif ($nature == 5) {
359  return $langs->trans('AccountingJournalType5');
360  } elseif ($nature == 4) {
361  return $langs->trans('AccountingJournalType4');
362  } elseif ($nature == 3) {
363  return $langs->trans('AccountingJournalType3');
364  } elseif ($nature == 2) {
365  return $langs->trans('AccountingJournalType2');
366  } elseif ($nature == 1) {
367  return $langs->trans('AccountingJournalType1');
368  }
369  }
370  }
371 
372 
383  public function getData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
384  {
385  global $hookmanager;
386 
387  // Clean parameters
388  if (empty($type)) $type = 'view';
389  if (empty($in_bookkeeping)) $in_bookkeeping = 'notyet';
390 
391  // Hook
392  if (!is_object($hookmanager)) {
393  include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
394  $hookmanager = new HookManager($this->db);
395  }
396 
397  $data = array();
398 
399  $hookmanager->initHooks(array('accountingjournaldao'));
400  $parameters = array('data' => &$data, 'user' => $user, 'type' => $type, 'date_start' => $date_start, 'date_end' => $date_end, 'in_bookkeeping' => $in_bookkeeping);
401  $reshook = $hookmanager->executeHooks('getData', $parameters, $this); // Note that $action and $object may have been
402  if ($reshook < 0) {
403  $this->error = $hookmanager->error;
404  $this->errors = $hookmanager->errors;
405  return -1;
406  } elseif (empty($reshook)) {
407  switch ($this->nature) {
408  case 1: // Various Journal
409  $data = $this->getAssetData($user, $type, $date_start, $date_end, $in_bookkeeping);
410  break;
411  // case 2: // Sells Journal
412  // case 3: // Purchases Journal
413  // case 4: // Bank Journal
414  // case 5: // Expense reports Journal
415  // case 8: // Inventory Journal
416  // case 9: // hasnew Journal
417  }
418  }
419 
420  return $data;
421  }
422 
433  public function getAssetData(User $user, $type = 'view', $date_start = null, $date_end = null, $in_bookkeeping = 'notyet')
434  {
435  global $conf, $langs;
436 
437  if (!isModEnabled('asset')) {
438  return array();
439  }
440 
441  require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
442  require_once DOL_DOCUMENT_ROOT . '/asset/class/asset.class.php';
443  require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
444  require_once DOL_DOCUMENT_ROOT . '/asset/class/assetdepreciationoptions.class.php';
445 
446  $langs->loadLangs(array("assets"));
447 
448  // Clean parameters
449  if (empty($type)) {
450  $type = 'view';
451  }
452  if (empty($in_bookkeeping)) {
453  $in_bookkeeping = 'notyet';
454  }
455 
456  $sql = "";
457 
458  // FIXME sql error with Mysql 5.7
459  /*if ($in_bookkeeping == 'already' || $in_bookkeeping == 'notyet') {
460  $sql .= "WITH in_accounting_bookkeeping(fk_docdet) AS (";
461  $sql .= " SELECT DISTINCT fk_docdet";
462  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping";
463  $sql .= " WHERE doc_type = 'asset'";
464  $sql .= ") ";
465  }*/
466 
467  $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";
468  $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";
469  $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";
470  $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation as ad";
471  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "asset as a ON a.rowid = ad.fk_asset";
472  // FIXME sql error with Mysql 5.7
473  /*if ($in_bookkeeping == 'already' || $in_bookkeeping == 'notyet') {
474  $sql .= " LEFT JOIN in_accounting_bookkeeping as iab ON iab.fk_docdet = ad.rowid";
475  }*/
476  $sql .= " WHERE a.entity IN (" . getEntity('asset', 0) . ')'; // We don't share object for accountancy, we use source object sharing
477  // Compatibility with Mysql 5.7
478  if ($in_bookkeeping == 'already') {
479  $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')";
480  } elseif ($in_bookkeeping == 'notyet') {
481  $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')";
482  }
483  $sql .= " AND ad.ref != ''"; // not reversal lines
484  if ($date_start && $date_end) {
485  $sql .= " AND ad.depreciation_date >= '" . $this->db->idate($date_start) . "' AND ad.depreciation_date <= '" . $this->db->idate($date_end) . "'";
486  }
487  // Define begin binding date
488  if (!empty($conf->global->ACCOUNTING_DATE_START_BINDING)) {
489  $sql .= " AND ad.depreciation_date >= '" . $this->db->idate($conf->global->ACCOUNTING_DATE_START_BINDING) . "'";
490  }
491  // Already in bookkeeping or not
492  // FIXME sql error with Mysql 5.7
493  /*if ($in_bookkeeping == 'already' || $in_bookkeeping == 'notyet') {
494  $sql .= " AND iab.fk_docdet IS" . ($in_bookkeeping == 'already' ? " NOT" : "") . " NULL";
495  }*/
496  $sql .= " ORDER BY ad.depreciation_date";
497 
498  dol_syslog(__METHOD__, LOG_DEBUG);
499  $resql = $this->db->query($sql);
500  if (!$resql) {
501  $this->errors[] = $this->db->lasterror();
502  return -1;
503  }
504 
505  $pre_data = array(
506  'elements' => array(),
507  );
508  while ($obj = $this->db->fetch_object($resql)) {
509  if (!isset($pre_data['elements'][$obj->rowid])) {
510  $pre_data['elements'][$obj->rowid] = array(
511  'ref' => $obj->asset_ref,
512  'label' => $obj->asset_label,
513  'acquisition_value_ht' => $obj->asset_acquisition_value_ht,
514  'depreciation' => array(),
515  );
516 
517  // Disposal infos
518  if (isset($obj->asset_disposal_date)) {
519  $pre_data['elements'][$obj->rowid]['disposal'] = array(
520  'date' => $this->db->jdate($obj->asset_disposal_date),
521  'amount' => $obj->asset_disposal_amount_ht,
522  'subject_to_vat' => !empty($obj->asset_disposal_subject_to_vat),
523  );
524  }
525  }
526 
527  $compta_debit = empty($obj->accountancy_code_debit) ? 'NotDefined' : $obj->accountancy_code_debit;
528  $compta_credit = empty($obj->accountancy_code_credit) ? 'NotDefined' : $obj->accountancy_code_credit;
529 
530  $pre_data['elements'][$obj->rowid]['depreciation'][$obj->depreciation_id] = array(
531  'date' => $this->db->jdate($obj->depreciation_date),
532  'ref' => $obj->depreciation_ref,
533  'lines' => array(
534  $compta_debit => -$obj->depreciation_ht,
535  $compta_credit => $obj->depreciation_ht,
536  ),
537  );
538  }
539 
540  $disposal_ref = $langs->transnoentitiesnoconv('AssetDisposal');
541  $journal = $this->code;
542  $journal_label = $this->label;
543  $journal_label_formatted = $langs->transnoentities($journal_label);
544  $now = dol_now();
545 
546  $element_static = new Asset($this->db);
547 
548  $journal_data = array();
549  foreach ($pre_data['elements'] as $pre_data_id => $pre_data_info) {
550  $element_static->id = $pre_data_id;
551  $element_static->ref = (string) $pre_data_info["ref"];
552  $element_static->label = (string) $pre_data_info["label"];
553  $element_static->acquisition_value_ht = $pre_data_info["acquisition_value_ht"];
554  $element_link = $element_static->getNomUrl(1, 'with_label');
555 
556  $element_name_formatted_0 = dol_trunc($element_static->label, 16);
557  $element_name_formatted_1 = utf8_decode(dol_trunc($element_static->label, 32));
558  $element_name_formatted_2 = utf8_decode(dol_trunc($element_static->label, 16));
559  $label_operation = $element_static->getNomUrl(0, 'label', 16);
560 
561  $element = array(
562  'ref' => dol_trunc($element_static->ref, 16, 'right', 'UTF-8', 1),
563  'error' => $pre_data_info['error'],
564  'blocks' => array(),
565  );
566 
567  // Depreciation lines
568  //--------------------
569  foreach ($pre_data_info['depreciation'] as $depreciation_id => $line) {
570  $depreciation_ref = $line["ref"];
571  $depreciation_date = $line["date"];
572  $depreciation_date_formatted = dol_print_date($depreciation_date, 'day');
573 
574  // lines
575  $blocks = array();
576  foreach ($line['lines'] as $account => $mt) {
577  $account_infos = $this->getAccountingAccountInfos($account);
578 
579  if ($type == 'view') {
580  $account_to_show = length_accounta($account);
581  if (($account_to_show == "") || $account_to_show == 'NotDefined') {
582  $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
583  }
584 
585  $blocks[] = array(
586  'date' => $depreciation_date_formatted,
587  'piece' => $element_link,
588  'account_accounting' => $account_to_show,
589  'subledger_account' => '',
590  'label_operation' => $label_operation . ' - ' . $depreciation_ref,
591  'debit' => $mt < 0 ? price(-$mt) : '',
592  'credit' => $mt >= 0 ? price($mt) : '',
593  );
594  } elseif ($type == 'bookkeeping') {
595  if ($account_infos['found']) {
596  $blocks[] = array(
597  'doc_date' => $depreciation_date,
598  'date_lim_reglement' => '',
599  'doc_ref' => $element_static->ref,
600  'date_creation' => $now,
601  'doc_type' => 'asset',
602  'fk_doc' => $element_static->id,
603  'fk_docdet' => $depreciation_id, // Useless, can be several lines that are source of this record to add
604  'thirdparty_code' => '',
605  'subledger_account' => '',
606  'subledger_label' => '',
607  'numero_compte' => $account,
608  'label_compte' => $account_infos['label'],
609  'label_operation' => $element_name_formatted_0 . ' - ' . $depreciation_ref,
610  'montant' => $mt,
611  'sens' => $mt < 0 ? 'D' : 'C',
612  'debit' => $mt < 0 ? -$mt : 0,
613  'credit' => $mt >= 0 ? $mt : 0,
614  'code_journal' => $journal,
615  'journal_label' => $journal_label_formatted,
616  'piece_num' => '',
617  'import_key' => '',
618  'fk_user_author' => $user->id,
619  'entity' => $conf->entity,
620  );
621  }
622  } else { // $type == 'csv'
623  $blocks[] = array(
624  $depreciation_date, // Date
625  $element_static->ref, // Piece
626  $account_infos['code_formatted_1'], // AccountAccounting
627  $element_name_formatted_0 . ' - ' . $depreciation_ref, // LabelOperation
628  $mt < 0 ? price(-$mt) : '', // Debit
629  $mt >= 0 ? price($mt) : '', // Credit
630  );
631  }
632  }
633  $element['blocks'][] = $blocks;
634  }
635 
636  // Disposal line
637  //--------------------
638  if (!empty($pre_data_info['disposal'])) {
639  $disposal_date = $pre_data_info['disposal']['date'];
640 
641  if ((!($date_start && $date_end) || ($date_start <= $disposal_date && $disposal_date <= $date_end)) &&
642  (empty($conf->global->ACCOUNTING_DATE_START_BINDING) || $conf->global->ACCOUNTING_DATE_START_BINDING <= $disposal_date)
643  ) {
644  $disposal_amount = $pre_data_info['disposal']['amount'];
645  $disposal_subject_to_vat = $pre_data_info['disposal']['subject_to_vat'];
646  $disposal_date_formatted = dol_print_date($disposal_date, 'day');
647  $disposal_vat = $conf->global->ASSET_DISPOSAL_VAT > 0 ? $conf->global->ASSET_DISPOSAL_VAT : 20;
648 
649  // Get accountancy codes
650  //---------------------------
651  require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
652  $accountancy_codes = new AssetAccountancyCodes($this->db);
653  $result = $accountancy_codes->fetchAccountancyCodes($element_static->id);
654  if ($result < 0) {
655  $element['error'] = $accountancy_codes->errorsToString();
656  } else {
657  // Get last depreciation cumulative amount
658  $element_static->fetchDepreciationLines();
659  foreach ($element_static->depreciation_lines as $mode_key => $depreciation_lines) {
660  $accountancy_codes_list = $accountancy_codes->accountancy_codes[$mode_key];
661 
662  if (!isset($accountancy_codes_list['value_asset_sold'])) {
663  continue;
664  }
665 
666  $accountancy_code_value_asset_sold = empty($accountancy_codes_list['value_asset_sold']) ? 'NotDefined' : $accountancy_codes_list['value_asset_sold'];
667  $accountancy_code_depreciation_asset = empty($accountancy_codes_list['depreciation_asset']) ? 'NotDefined' : $accountancy_codes_list['depreciation_asset'];
668  $accountancy_code_asset = empty($accountancy_codes_list['asset']) ? 'NotDefined' : $accountancy_codes_list['asset'];
669  $accountancy_code_receivable_on_assignment = empty($accountancy_codes_list['receivable_on_assignment']) ? 'NotDefined' : $accountancy_codes_list['receivable_on_assignment'];
670  $accountancy_code_vat_collected = empty($accountancy_codes_list['vat_collected']) ? 'NotDefined' : $accountancy_codes_list['vat_collected'];
671  $accountancy_code_proceeds_from_sales = empty($accountancy_codes_list['proceeds_from_sales']) ? 'NotDefined' : $accountancy_codes_list['proceeds_from_sales'];
672 
673  $last_cumulative_amount_ht = 0;
674  $depreciated_ids = array_keys($pre_data_info['depreciation']);
675  foreach ($depreciation_lines as $line) {
676  $last_cumulative_amount_ht = $line['cumulative_depreciation_ht'];
677  if (!in_array($line['id'], $depreciated_ids) && empty($line['bookkeeping']) && !empty($line['ref'])) {
678  break;
679  }
680  }
681 
682  $lines = array();
683  $lines[0][$accountancy_code_value_asset_sold] = -($element_static->acquisition_value_ht - $last_cumulative_amount_ht);
684  $lines[0][$accountancy_code_depreciation_asset] = -$last_cumulative_amount_ht;
685  $lines[0][$accountancy_code_asset] = $element_static->acquisition_value_ht;
686 
687  $disposal_amount_vat = $disposal_subject_to_vat ? (double) price2num($disposal_amount * $disposal_vat / 100, 'MT') : 0;
688  $lines[1][$accountancy_code_receivable_on_assignment] = -($disposal_amount + $disposal_amount_vat);
689  if ($disposal_subject_to_vat) $lines[1][$accountancy_code_vat_collected] = $disposal_amount_vat;
690  $lines[1][$accountancy_code_proceeds_from_sales] = $disposal_amount;
691 
692  foreach ($lines as $lines_block) {
693  $blocks = array();
694  foreach ($lines_block as $account => $mt) {
695  $account_infos = $this->getAccountingAccountInfos($account);
696 
697  if ($type == 'view') {
698  $account_to_show = length_accounta($account);
699  if (($account_to_show == "") || $account_to_show == 'NotDefined') {
700  $account_to_show = '<span class="error">' . $langs->trans("AssetInAccountNotDefined") . '</span>';
701  }
702 
703  $blocks[] = array(
704  'date' => $disposal_date_formatted,
705  'piece' => $element_link,
706  'account_accounting' => $account_to_show,
707  'subledger_account' => '',
708  'label_operation' => $label_operation . ' - ' . $disposal_ref,
709  'debit' => $mt < 0 ? price(-$mt) : '',
710  'credit' => $mt >= 0 ? price($mt) : '',
711  );
712  } elseif ($type == 'bookkeeping') {
713  if ($account_infos['found']) {
714  $blocks[] = array(
715  'doc_date' => $disposal_date,
716  'date_lim_reglement' => '',
717  'doc_ref' => $element_static->ref,
718  'date_creation' => $now,
719  'doc_type' => 'asset',
720  'fk_doc' => $element_static->id,
721  'fk_docdet' => 0, // Useless, can be several lines that are source of this record to add
722  'thirdparty_code' => '',
723  'subledger_account' => '',
724  'subledger_label' => '',
725  'numero_compte' => $account,
726  'label_compte' => $account_infos['label'],
727  'label_operation' => $element_name_formatted_0 . ' - ' . $disposal_ref,
728  'montant' => $mt,
729  'sens' => $mt < 0 ? 'D' : 'C',
730  'debit' => $mt < 0 ? -$mt : 0,
731  'credit' => $mt >= 0 ? $mt : 0,
732  'code_journal' => $journal,
733  'journal_label' => $journal_label_formatted,
734  'piece_num' => '',
735  'import_key' => '',
736  'fk_user_author' => $user->id,
737  'entity' => $conf->entity,
738  );
739  }
740  } else { // $type == 'csv'
741  $blocks[] = array(
742  $disposal_date, // Date
743  $element_static->ref, // Piece
744  $account_infos['code_formatted_1'], // AccountAccounting
745  $element_name_formatted_0 . ' - ' . $disposal_ref, // LabelOperation
746  $mt < 0 ? price(-$mt) : '', // Debit
747  $mt >= 0 ? price($mt) : '', // Credit
748  );
749  }
750  }
751  $element['blocks'][] = $blocks;
752  }
753  }
754  }
755  }
756  }
757 
758  $journal_data[$pre_data_id] = $element;
759  }
760  unset($pre_data);
761 
762  return $journal_data;
763  }
764 
808  public function writeIntoBookkeeping(User $user, &$journal_data = array(), $max_nb_errors = 10)
809  {
810  global $conf, $langs, $hookmanager;
811  require_once DOL_DOCUMENT_ROOT . '/accountancy/class/bookkeeping.class.php';
812 
813  // Hook
814  if (!is_object($hookmanager)) {
815  include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
816  $hookmanager = new HookManager($this->db);
817  }
818 
819  $error = 0;
820 
821  $hookmanager->initHooks(array('accountingjournaldao'));
822  $parameters = array('journal_data' => &$journal_data);
823  $reshook = $hookmanager->executeHooks('writeBookkeeping', $parameters, $this); // Note that $action and $object may have been
824  if ($reshook < 0) {
825  $this->error = $hookmanager->error;
826  $this->errors = $hookmanager->errors;
827  return -1;
828  } elseif (empty($reshook)) {
829  // Clean parameters
830  $journal_data = is_array($journal_data) ? $journal_data : array();
831 
832  foreach ($journal_data as $element_id => $element) {
833  $error_for_line = 0;
834  $total_credit = 0;
835  $total_debit = 0;
836 
837  $this->db->begin();
838 
839  if ($element['error'] == 'somelinesarenotbound') {
840  $error++;
841  $error_for_line++;
842  $this->errors[] = $langs->trans('ErrorInvoiceContainsLinesNotYetBounded', $element['ref']);
843  }
844 
845  if (!$error_for_line) {
846  foreach ($element['blocks'] as $lines) {
847  foreach ($lines as $line) {
848  $bookkeeping = new BookKeeping($this->db);
849  $bookkeeping->doc_date = $line['doc_date'];
850  $bookkeeping->date_lim_reglement = $line['date_lim_reglement'];
851  $bookkeeping->doc_ref = $line['doc_ref'];
852  $bookkeeping->date_creation = $line['date_creation']; // not used
853  $bookkeeping->doc_type = $line['doc_type'];
854  $bookkeeping->fk_doc = $line['fk_doc'];
855  $bookkeeping->fk_docdet = $line['fk_docdet'];
856  $bookkeeping->thirdparty_code = $line['thirdparty_code'];
857  $bookkeeping->subledger_account = $line['subledger_account'];
858  $bookkeeping->subledger_label = $line['subledger_label'];
859  $bookkeeping->numero_compte = $line['numero_compte'];
860  $bookkeeping->label_compte = $line['label_compte'];
861  $bookkeeping->label_operation = $line['label_operation'];
862  $bookkeeping->montant = $line['montant'];
863  $bookkeeping->sens = $line['sens'];
864  $bookkeeping->debit = $line['debit'];
865  $bookkeeping->credit = $line['credit'];
866  $bookkeeping->code_journal = $line['code_journal'];
867  $bookkeeping->journal_label = $line['journal_label'];
868  $bookkeeping->piece_num = $line['piece_num'];
869  $bookkeeping->import_key = $line['import_key'];
870  $bookkeeping->fk_user_author = $user->id;
871  $bookkeeping->entity = $conf->entity;
872 
873  $total_debit += $bookkeeping->debit;
874  $total_credit += $bookkeeping->credit;
875 
876  $result = $bookkeeping->create($user);
877  if ($result < 0) {
878  if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists') { // Already exists
879  $error++;
880  $error_for_line++;
881  $journal_data[$element_id]['error'] = 'alreadyjournalized';
882  } else {
883  $error++;
884  $error_for_line++;
885  $journal_data[$element_id]['error'] = 'other';
886  $this->errors[] = $bookkeeping->errorsToString();
887  }
888  }
889  //
890  // if (!$error_for_line && isModEnabled('asset') && $this->nature == 1 && $bookkeeping->fk_doc > 0) {
891  // // Set last cumulative depreciation
892  // require_once DOL_DOCUMENT_ROOT . '/asset/class/asset.class.php';
893  // $asset = new Asset($this->db);
894  // $result = $asset->setLastCumulativeDepreciation($bookkeeping->fk_doc);
895  // if ($result < 0) {
896  // $error++;
897  // $error_for_line++;
898  // $journal_data[$element_id]['error'] = 'other';
899  // $this->errors[] = $asset->errorsToString();
900  // }
901  // }
902  }
903 
904  if ($error_for_line) {
905  break;
906  }
907  }
908  }
909 
910  // Protection against a bug on lines before
911  if (!$error_for_line && (price2num($total_debit, 'MT') != price2num($total_credit, 'MT'))) {
912  $error++;
913  $error_for_line++;
914  $journal_data[$element_id]['error'] = 'amountsnotbalanced';
915  $this->errors[] = 'Try to insert a non balanced transaction in book for ' . $element['blocks'] . '. Canceled. Surely a bug.';
916  }
917 
918  if (!$error_for_line) {
919  $this->db->commit();
920  } else {
921  $this->db->rollback();
922 
923  if ($error >= $max_nb_errors) {
924  $this->errors[] = $langs->trans("ErrorTooManyErrorsProcessStopped");
925  break; // Break in the foreach
926  }
927  }
928  }
929  }
930 
931  return $error ? -$error : 1;
932  }
933 
955  public function exportCsv(&$journal_data = array(), $search_date_end = 0, $sep = '')
956  {
957  global $conf, $langs, $hookmanager;
958 
959  if (empty($sep)) $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
960  $out = '';
961 
962  // Hook
963  if (!is_object($hookmanager)) {
964  include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
965  $hookmanager = new HookManager($this->db);
966  }
967 
968  $hookmanager->initHooks(array('accountingjournaldao'));
969  $parameters = array('journal_data' => &$journal_data, 'search_date_end' => &$search_date_end, 'sep' => &$sep, 'out' => &$out);
970  $reshook = $hookmanager->executeHooks('exportCsv', $parameters, $this); // Note that $action and $object may have been
971  if ($reshook < 0) {
972  $this->error = $hookmanager->error;
973  $this->errors = $hookmanager->errors;
974  return -1;
975  } elseif (empty($reshook)) {
976  // Clean parameters
977  $journal_data = is_array($journal_data) ? $journal_data : array();
978 
979  // CSV header line
980  $header = array();
981  if ($this->nature == 4) {
982  $header = array(
983  $langs->transnoentitiesnoconv("BankId"),
984  $langs->transnoentitiesnoconv("Date"),
985  $langs->transnoentitiesnoconv("PaymentMode"),
986  $langs->transnoentitiesnoconv("AccountAccounting"),
987  $langs->transnoentitiesnoconv("LedgerAccount"),
988  $langs->transnoentitiesnoconv("SubledgerAccount"),
989  $langs->transnoentitiesnoconv("Label"),
990  $langs->transnoentitiesnoconv("AccountingDebit"),
991  $langs->transnoentitiesnoconv("AccountingCredit"),
992  $langs->transnoentitiesnoconv("Journal"),
993  $langs->transnoentitiesnoconv("Note"),
994  );
995  } elseif ($this->nature == 5) {
996  $header = array(
997  $langs->transnoentitiesnoconv("Date"),
998  $langs->transnoentitiesnoconv("Piece"),
999  $langs->transnoentitiesnoconv("AccountAccounting"),
1000  $langs->transnoentitiesnoconv("LabelOperation"),
1001  $langs->transnoentitiesnoconv("AccountingDebit"),
1002  $langs->transnoentitiesnoconv("AccountingCredit"),
1003  );
1004  } elseif ($this->nature == 1) {
1005  $header = array(
1006  $langs->transnoentitiesnoconv("Date"),
1007  $langs->transnoentitiesnoconv("Piece"),
1008  $langs->transnoentitiesnoconv("AccountAccounting"),
1009  $langs->transnoentitiesnoconv("LabelOperation"),
1010  $langs->transnoentitiesnoconv("AccountingDebit"),
1011  $langs->transnoentitiesnoconv("AccountingCredit"),
1012  );
1013  }
1014 
1015  if (!empty($header)) $out .= '"' . implode('"' . $sep . '"', $header) . '"' . "\n";
1016  foreach ($journal_data as $element_id => $element) {
1017  foreach ($element['blocks'] as $lines) {
1018  foreach ($lines as $line) {
1019  $out .= '"' . implode('"' . $sep . '"', $line) . '"' . "\n";
1020  }
1021  }
1022  }
1023  }
1024 
1025  return $out;
1026  }
1027 
1034  public function getAccountingAccountInfos($account)
1035  {
1036  if (!isset(self::$accounting_account_cached[$account])) {
1037  require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
1038  require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountingaccount.class.php';
1039  $accountingaccount = new AccountingAccount($this->db);
1040  $result = $accountingaccount->fetch(null, $account, true);
1041  if ($result > 0) {
1042  self::$accounting_account_cached[$account] = array(
1043  'found' => true,
1044  'label' => $accountingaccount->label,
1045  'code_formatted_1' => length_accounta(html_entity_decode($account)),
1046  'label_formatted_1' => utf8_decode(dol_trunc($accountingaccount->label, 32)),
1047  'label_formatted_2' => dol_trunc($accountingaccount->label, 32),
1048  );
1049  } else {
1050  self::$accounting_account_cached[$account] = array(
1051  'found' => false,
1052  'label' => '',
1053  'code_formatted_1' => length_accounta(html_entity_decode($account)),
1054  'label_formatted_1' => '',
1055  'label_formatted_2' => '',
1056  );
1057  }
1058  }
1059 
1060  return self::$accounting_account_cached[$account];
1061  }
1062 }
length_accounta($accounta)
Return Auxiliary accounting account of thirdparties with defined length.
$object ref
Definition: info.php:78
Class to manage accounting accounts.
Class to manage accounting accounts.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load object in memory from the database.
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 !
getNomUrl($withpicto=0, $withlabel=0, $nourl=0, $moretitle='', $notooltip=0)
Return clicable name (with picto eventually)
__construct($db)
Constructor.
LibType($nature, $mode=0)
Return type of an accounting journal.
getAccountingAccountInfos($account)
Get accounting account infos.
fetch($rowid=null, $journal_code=null)
Load an object from database.
getAssetData(User $user, $type='view', $date_start=null, $date_end=null, $in_bookkeeping='notyet')
Get asset data for various journal.
getLibType($mode=0)
Retourne le libelle du statut d'un user (actif, inactif)
Class for AssetAccountancyCodes.
Class for Asset.
Definition: asset.class.php:31
Class to manage Ledger (General Ledger and Subledger)
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class to manage hooks.
Class to manage Dolibarr users.
Definition: user.class.php:47
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
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.
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.
$conf db
API class for accounts.
Definition: inc.php:41
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...