dolibarr 19.0.3
bookkeeping.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2014-2017 Olivier Geffroy <jeff@jeffinfo.com>
3 * Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
4 * Copyright (C) 2015-2020 Florian Henry <florian.henry@open-concept.pro>
5 * Copyright (C) 2018-2020 Frédéric France <frederic.france@netlogic.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27// Class
28require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29require_once DOL_DOCUMENT_ROOT.'/core/class/fiscalyear.class.php';
30require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
31require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
32
37{
41 public $element = 'accountingbookkeeping';
42
46 public $table_element = 'accounting_bookkeeping';
47
51 public $entity;
52
56 public $lines = array();
57
61 public $id;
62
66 public $doc_date;
67
71 public $date_lim_reglement;
72
76 public $doc_type;
77
81 public $doc_ref;
82
86 public $fk_doc;
87
91 public $fk_docdet;
92
96 public $thirdparty_code;
97
101 public $subledger_account;
102
106 public $subledger_label;
107
111 public $numero_compte;
112
116 public $label_compte;
117
121 public $label_operation;
122
126 public $debit;
127
131 public $credit;
132
137 public $montant;
138
143 public $amount;
144
148 public $sens;
149
153 public $fk_user_author;
154
158 public $import_key;
159
163 public $code_journal;
164
168 public $journal_label;
169
173 public $piece_num;
174
178 public $linesmvt = array();
179
183 public $linesexport = array();
184
188 public $date_validation;
189
193 public $date_export;
194
198 public $picto = 'generic';
199
203 public static $can_modify_bookkeeping_sql_cached;
204
205
211 public function __construct(DoliDB $db)
212 {
213 $this->db = $db;
214 }
215
223 public function create(User $user, $notrigger = false)
224 {
225 global $conf, $langs;
226
227 dol_syslog(__METHOD__, LOG_DEBUG);
228
229 $error = 0;
230
231 // Clean parameters</center>
232 if (isset($this->doc_type)) {
233 $this->doc_type = trim($this->doc_type);
234 }
235 if (isset($this->doc_ref)) {
236 $this->doc_ref = trim($this->doc_ref);
237 }
238 if (isset($this->fk_doc)) {
239 $this->fk_doc = (int) $this->fk_doc;
240 }
241 if (isset($this->fk_docdet)) {
242 $this->fk_docdet = (int) $this->fk_docdet;
243 }
244 if (isset($this->thirdparty_code)) {
245 $this->thirdparty_code = trim($this->thirdparty_code);
246 }
247 if (isset($this->subledger_account)) {
248 $this->subledger_account = trim($this->subledger_account);
249 }
250 if (isset($this->subledger_label)) {
251 $this->subledger_label = trim($this->subledger_label);
252 }
253 if (isset($this->numero_compte)) {
254 $this->numero_compte = trim($this->numero_compte);
255 }
256 if (isset($this->label_compte)) {
257 $this->label_compte = trim($this->label_compte);
258 }
259 if (isset($this->label_operation)) {
260 $this->label_operation = trim($this->label_operation);
261 }
262 if (isset($this->debit)) {
263 $this->debit = (float) $this->debit;
264 }
265 if (isset($this->credit)) {
266 $this->credit = (float) $this->credit;
267 }
268 if (isset($this->montant)) {
269 $this->montant = (float) $this->montant;
270 }
271 if (isset($this->amount)) {
272 $this->amount = (float) $this->amount;
273 }
274 if (isset($this->sens)) {
275 $this->sens = trim($this->sens);
276 }
277 if (isset($this->import_key)) {
278 $this->import_key = trim($this->import_key);
279 }
280 if (isset($this->code_journal)) {
281 $this->code_journal = trim($this->code_journal);
282 }
283 if (isset($this->journal_label)) {
284 $this->journal_label = trim($this->journal_label);
285 }
286 if (isset($this->piece_num)) {
287 $this->piece_num = trim($this->piece_num);
288 }
289 if (empty($this->debit)) {
290 $this->debit = 0.0;
291 }
292 if (empty($this->credit)) {
293 $this->credit = 0.0;
294 }
295
296 $result = $this->validBookkeepingDate($this->doc_date);
297 if ($result < 0) {
298 return -1;
299 } elseif ($result == 0) {
300 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
301 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
302 } else {
303 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
304 }
305 return -1;
306 }
307
308 // Check parameters
309 if (($this->numero_compte == "") || $this->numero_compte == '-1' || $this->numero_compte == 'NotDefined') {
310 $langs->loadLangs(array("errors"));
311 if (in_array($this->doc_type, array('bank', 'expense_report'))) {
312 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet, $this->doc_type);
313 } else {
314 //$this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->doc_ref, $this->label_compte);
315 $mesg = $this->doc_ref.', '.$langs->trans("AccountAccounting").': '.($this->numero_compte != -1 ? $this->numero_compte : $langs->trans("Unknown"));
316 if ($this->subledger_account && $this->subledger_account != $this->numero_compte) {
317 $mesg .= ', '.$langs->trans("SubledgerAccount").': '.$this->subledger_account;
318 }
319 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForLine', $mesg);
320 }
321
322 return -1;
323 }
324
325 $this->db->begin();
326
327 $this->piece_num = 0;
328
329 // First check if line not yet already in bookkeeping.
330 // Note that we must include 'doc_type - fk_doc - numero_compte - label' to be sure to have unicity of line (because we may have several lines
331 // with same doc_type, fk_doc, numero_compte for 1 invoice line when using localtaxes with same account)
332 // WARNING: This is not reliable, label may have been modified. This is just a small protection.
333 // The page that make transfer make the test on couple (doc_type - fk_doc) only.
334 $sql = "SELECT count(*) as nb";
335 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
336 $sql .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'";
337 $sql .= " AND fk_doc = ".((int) $this->fk_doc);
338 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
339 // DO NOT USE THIS IN PRODUCTION. This will generate a lot of trouble into reports and will corrupt database (by generating duplicate entries.
340 $sql .= " AND fk_docdet = ".((int) $this->fk_docdet); // This field can be 0 if record is for several lines
341 }
342 $sql .= " AND numero_compte = '".$this->db->escape($this->numero_compte)."'";
343 $sql .= " AND label_operation = '".$this->db->escape($this->label_operation)."'";
344 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
345
346 $resql = $this->db->query($sql);
347
348 if ($resql) {
349 $row = $this->db->fetch_object($resql);
350 if ($row->nb == 0) { // Not already into bookkeeping
351 // Check to know if piece_num already exists for data we try to insert to reuse the same value
352 $sqlnum = "SELECT piece_num";
353 $sqlnum .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
354 $sqlnum .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'"; // For example doc_type = 'bank'
355 $sqlnum .= " AND fk_doc = ".((int) $this->fk_doc);
356 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
357 // fk_docdet is rowid into llx_bank or llx_facturedet or llx_facturefourndet, or ...
358 $sqlnum .= " AND fk_docdet = ".((int) $this->fk_docdet);
359 }
360 $sqlnum .= " AND doc_ref = '".$this->db->escape($this->doc_ref)."'"; // ref of source object
361 $sqlnum .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
362
363 dol_syslog(get_class($this).":: create sqlnum=".$sqlnum, LOG_DEBUG);
364 $resqlnum = $this->db->query($sqlnum);
365 if ($resqlnum) {
366 $objnum = $this->db->fetch_object($resqlnum);
367 $this->piece_num = $objnum->piece_num;
368 }
369
370 dol_syslog(get_class($this)."::create this->piece_num=".$this->piece_num, LOG_DEBUG);
371 if (empty($this->piece_num)) {
372 $sqlnum = "SELECT MAX(piece_num)+1 as maxpiecenum";
373 $sqlnum .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
374 $sqlnum .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
375
376 $resqlnum = $this->db->query($sqlnum);
377 if ($resqlnum) {
378 $objnum = $this->db->fetch_object($resqlnum);
379 $this->piece_num = $objnum->maxpiecenum;
380 }
381 dol_syslog(get_class($this).":: create now this->piece_num=".$this->piece_num, LOG_DEBUG);
382 }
383 if (empty($this->piece_num)) {
384 $this->piece_num = 1;
385 }
386
387 $now = dol_now();
388
389 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
390 $sql .= "doc_date";
391 $sql .= ", date_lim_reglement";
392 $sql .= ", doc_type";
393 $sql .= ", doc_ref";
394 $sql .= ", fk_doc";
395 $sql .= ", fk_docdet";
396 $sql .= ", thirdparty_code";
397 $sql .= ", subledger_account";
398 $sql .= ", subledger_label";
399 $sql .= ", numero_compte";
400 $sql .= ", label_compte";
401 $sql .= ", label_operation";
402 $sql .= ", debit";
403 $sql .= ", credit";
404 $sql .= ", montant";
405 $sql .= ", sens";
406 $sql .= ", fk_user_author";
407 $sql .= ", date_creation";
408 $sql .= ", code_journal";
409 $sql .= ", journal_label";
410 $sql .= ", piece_num";
411 $sql .= ', entity';
412 $sql .= ") VALUES (";
413 $sql .= "'".$this->db->idate($this->doc_date)."'";
414 $sql .= ", ".(!isset($this->date_lim_reglement) || dol_strlen($this->date_lim_reglement) == 0 ? 'NULL' : "'".$this->db->idate($this->date_lim_reglement)."'");
415 $sql .= ", '".$this->db->escape($this->doc_type)."'";
416 $sql .= ", '".$this->db->escape($this->doc_ref)."'";
417 $sql .= ", ".((int) $this->fk_doc);
418 $sql .= ", ".((int) $this->fk_docdet);
419 $sql .= ", ".(!empty($this->thirdparty_code) ? ("'".$this->db->escape($this->thirdparty_code)."'") : "NULL");
420 $sql .= ", ".(!empty($this->subledger_account) ? ("'".$this->db->escape($this->subledger_account)."'") : "NULL");
421 $sql .= ", ".(!empty($this->subledger_label) ? ("'".$this->db->escape($this->subledger_label)."'") : "NULL");
422 $sql .= ", '".$this->db->escape($this->numero_compte)."'";
423 $sql .= ", ".(!empty($this->label_compte) ? ("'".$this->db->escape($this->label_compte)."'") : "NULL");
424 $sql .= ", '".$this->db->escape($this->label_operation)."'";
425 $sql .= ", ".((float) $this->debit);
426 $sql .= ", ".((float) $this->credit);
427 $sql .= ", ".((float) $this->montant);
428 $sql .= ", ".(!empty($this->sens) ? ("'".$this->db->escape($this->sens)."'") : "NULL");
429 $sql .= ", '".$this->db->escape($this->fk_user_author)."'";
430 $sql .= ", '".$this->db->idate($now)."'";
431 $sql .= ", '".$this->db->escape($this->code_journal)."'";
432 $sql .= ", ".(!empty($this->journal_label) ? ("'".$this->db->escape($this->journal_label)."'") : "NULL");
433 $sql .= ", ".((int) $this->piece_num);
434 $sql .= ", ".(!isset($this->entity) ? $conf->entity : $this->entity);
435 $sql .= ")";
436
437 $resql = $this->db->query($sql);
438 if ($resql) {
439 $id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
440
441 if ($id > 0) {
442 $this->id = $id;
443 $result = 0;
444 } else {
445 $result = -2;
446 $error++;
447 $this->errors[] = 'Error Create Error '.$result.' lecture ID';
448 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
449 }
450 } else {
451 $result = -1;
452 $error++;
453 $this->errors[] = 'Error '.$this->db->lasterror();
454 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
455 }
456 } else { // Already exists
457 $result = -3;
458 $error++;
459 $this->error = 'BookkeepingRecordAlreadyExists';
460 dol_syslog(__METHOD__.' '.$this->error, LOG_WARNING);
461 }
462 } else {
463 $result = -5;
464 $error++;
465 $this->errors[] = 'Error '.$this->db->lasterror();
466 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
467 }
468
469 // Uncomment this and change MYOBJECT to your own tag if you
470 // want this action to call a trigger.
471 //if (! $error && ! $notrigger) {
472
473 // // Call triggers
474 // $result=$this->call_trigger('MYOBJECT_CREATE',$user);
475 // if ($result < 0) $error++;
476 // // End call triggers
477 //}
478
479 // Commit or rollback
480 if ($error) {
481 $this->db->rollback();
482 return -1 * $error;
483 } else {
484 $this->db->commit();
485 return $result;
486 }
487 }
488
499 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
500 {
501 global $db, $conf, $langs;
502 global $dolibarr_main_authentication, $dolibarr_main_demo;
503 global $menumanager, $hookmanager;
504
505 if (!empty($conf->dol_no_mouse_hover)) {
506 $notooltip = 1; // Force disable tooltips
507 }
508
509 $result = '';
510 $companylink = '';
511
512 $label = '<u>'.$langs->trans("Transaction").'</u>';
513 $label .= '<br>';
514 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->piece_num;
515
516 $url = DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?piece_num='.$this->piece_num;
517
518 if ($option != 'nolink') {
519 // Add param to save lastsearch_values or not
520 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
521 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
522 $add_save_lastsearch_values = 1;
523 }
524 if ($add_save_lastsearch_values) {
525 $url .= '&save_lastsearch_values=1';
526 }
527 }
528
529 $linkclose = '';
530 if (empty($notooltip)) {
531 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
532 $label = $langs->trans("ShowTransaction");
533 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
534 }
535 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
536 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
537 } else {
538 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
539 }
540
541 $linkstart = '<a href="'.$url.'"';
542 $linkstart .= $linkclose.'>';
543 $linkend = '</a>';
544
545 $result .= $linkstart;
546 if ($withpicto) {
547 $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);
548 }
549 if ($withpicto != 2) {
550 $result .= $this->piece_num;
551 }
552 $result .= $linkend;
553 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
554
555 global $action;
556 $hookmanager->initHooks(array($this->element . 'dao'));
557 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
558 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
559 if ($reshook > 0) {
560 $result = $hookmanager->resPrint;
561 } else {
562 $result .= $hookmanager->resPrint;
563 }
564 return $result;
565 }
566
575 public function createStd(User $user, $notrigger = false, $mode = '')
576 {
577 global $conf, $langs;
578
579 $langs->loadLangs(array("accountancy", "bills", "compta"));
580
581 dol_syslog(__METHOD__, LOG_DEBUG);
582
583 $error = 0;
584
585 // Clean parameters
586 if (isset($this->doc_type)) {
587 $this->doc_type = trim($this->doc_type);
588 }
589 if (isset($this->doc_ref)) {
590 $this->doc_ref = trim($this->doc_ref);
591 }
592 if (isset($this->fk_doc)) {
593 $this->fk_doc = (int) $this->fk_doc;
594 }
595 if (isset($this->fk_docdet)) {
596 $this->fk_docdet = (int) $this->fk_docdet;
597 }
598 if (isset($this->thirdparty_code)) {
599 $this->thirdparty_code = trim($this->thirdparty_code);
600 }
601 if (isset($this->subledger_account)) {
602 $this->subledger_account = trim($this->subledger_account);
603 }
604 if (isset($this->subledger_label)) {
605 $this->subledger_label = trim($this->subledger_label);
606 }
607 if (isset($this->numero_compte)) {
608 $this->numero_compte = trim($this->numero_compte);
609 }
610 if (isset($this->label_compte)) {
611 $this->label_compte = trim($this->label_compte);
612 }
613 if (isset($this->label_operation)) {
614 $this->label_operation = trim($this->label_operation);
615 }
616 if (isset($this->debit)) {
617 $this->debit = trim($this->debit);
618 }
619 if (isset($this->credit)) {
620 $this->credit = trim($this->credit);
621 }
622 if (isset($this->montant)) {
623 $this->montant = trim($this->montant);
624 }
625 if (isset($this->amount)) {
626 $this->amount = trim($this->amount);
627 }
628 if (isset($this->sens)) {
629 $this->sens = trim($this->sens);
630 }
631 if (isset($this->import_key)) {
632 $this->import_key = trim($this->import_key);
633 }
634 if (isset($this->code_journal)) {
635 $this->code_journal = trim($this->code_journal);
636 }
637 if (isset($this->journal_label)) {
638 $this->journal_label = trim($this->journal_label);
639 }
640 if (isset($this->piece_num)) {
641 $this->piece_num = trim($this->piece_num);
642 }
643 if (empty($this->debit)) {
644 $this->debit = 0;
645 }
646 if (empty($this->credit)) {
647 $this->credit = 0;
648 }
649 if (empty($this->montant)) {
650 $this->montant = 0;
651 }
652
653 $result = $this->validBookkeepingDate($this->doc_date);
654 if ($result < 0) {
655 return -1;
656 } elseif ($result == 0) {
657 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
658 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
659 } else {
660 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
661 }
662 return -1;
663 }
664
665 $this->debit = price2num($this->debit, 'MT');
666 $this->credit = price2num($this->credit, 'MT');
667 $this->montant = price2num($this->montant, 'MT');
668
669 $now = dol_now();
670
671 // Check parameters
672 $this->journal_label = $langs->trans($this->journal_label);
673
674 // Insert request
675 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.$mode.' (';
676 $sql .= 'doc_date,';
677 $sql .= 'date_lim_reglement,';
678 $sql .= 'doc_type,';
679 $sql .= 'doc_ref,';
680 $sql .= 'fk_doc,';
681 $sql .= 'fk_docdet,';
682 $sql .= 'thirdparty_code,';
683 $sql .= 'subledger_account,';
684 $sql .= 'subledger_label,';
685 $sql .= 'numero_compte,';
686 $sql .= 'label_compte,';
687 $sql .= 'label_operation,';
688 $sql .= 'debit,';
689 $sql .= 'credit,';
690 $sql .= 'montant,';
691 $sql .= 'sens,';
692 $sql .= 'fk_user_author,';
693 $sql .= 'date_creation,';
694 $sql .= 'code_journal,';
695 $sql .= 'journal_label,';
696 $sql .= 'piece_num,';
697 $sql .= 'entity';
698 $sql .= ') VALUES (';
699 $sql .= ' '.(!isset($this->doc_date) || dol_strlen($this->doc_date) == 0 ? 'NULL' : "'".$this->db->idate($this->doc_date)."'").',';
700 $sql .= ' '.(!isset($this->date_lim_reglement) || dol_strlen($this->date_lim_reglement) == 0 ? 'NULL' : "'".$this->db->idate($this->date_lim_reglement)."'").',';
701 $sql .= ' '.(!isset($this->doc_type) ? 'NULL' : "'".$this->db->escape($this->doc_type)."'").',';
702 $sql .= ' '.(!isset($this->doc_ref) ? 'NULL' : "'".$this->db->escape($this->doc_ref)."'").',';
703 $sql .= ' '.(empty($this->fk_doc) ? '0' : (int) $this->fk_doc).',';
704 $sql .= ' '.(empty($this->fk_docdet) ? '0' : (int) $this->fk_docdet).',';
705 $sql .= ' '.(!isset($this->thirdparty_code) ? 'NULL' : "'".$this->db->escape($this->thirdparty_code)."'").',';
706 $sql .= ' '.(!isset($this->subledger_account) ? 'NULL' : "'".$this->db->escape($this->subledger_account)."'").',';
707 $sql .= ' '.(!isset($this->subledger_label) ? 'NULL' : "'".$this->db->escape($this->subledger_label)."'").',';
708 $sql .= ' '.(!isset($this->numero_compte) ? 'NULL' : "'".$this->db->escape($this->numero_compte)."'").',';
709 $sql .= ' '.(!isset($this->label_compte) ? 'NULL' : "'".$this->db->escape($this->label_compte)."'").',';
710 $sql .= ' '.(!isset($this->label_operation) ? 'NULL' : "'".$this->db->escape($this->label_operation)."'").',';
711 $sql .= ' '.(!isset($this->debit) ? 'NULL' : $this->debit).',';
712 $sql .= ' '.(!isset($this->credit) ? 'NULL' : $this->credit).',';
713 $sql .= ' '.(!isset($this->montant) ? 'NULL' : $this->montant).',';
714 $sql .= ' '.(!isset($this->sens) ? 'NULL' : "'".$this->db->escape($this->sens)."'").',';
715 $sql .= ' '.((int) $user->id).',';
716 $sql .= ' '."'".$this->db->idate($now)."',";
717 $sql .= ' '.(empty($this->code_journal) ? 'NULL' : "'".$this->db->escape($this->code_journal)."'").',';
718 $sql .= ' '.(empty($this->journal_label) ? 'NULL' : "'".$this->db->escape($this->journal_label)."'").',';
719 $sql .= ' '.(empty($this->piece_num) ? 'NULL' : $this->db->escape($this->piece_num)).',';
720 $sql .= ' '.(!isset($this->entity) ? $conf->entity : $this->entity);
721 $sql .= ')';
722
723 $this->db->begin();
724
725 $resql = $this->db->query($sql);
726 if (!$resql) {
727 $error++;
728 $this->errors[] = 'Error '.$this->db->lasterror();
729 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
730 }
731
732 if (!$error) {
733 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element.$mode);
734
735 // Uncomment this and change MYOBJECT to your own tag if you
736 // want this action to call a trigger.
737 //if (! $notrigger) {
738
739 // // Call triggers
740 // $result=$this->call_trigger('MYOBJECT_CREATE',$user);
741 // if ($result < 0) $error++;
742 // // End call triggers
743 //}
744 }
745
746 // Commit or rollback
747 if ($error) {
748 $this->db->rollback();
749
750 return -1 * $error;
751 } else {
752 $this->db->commit();
753
754 return $this->id;
755 }
756 }
757
766 public function fetch($id, $ref = null, $mode = '')
767 {
768 global $conf;
769
770 dol_syslog(__METHOD__, LOG_DEBUG);
771
772 $sql = 'SELECT';
773 $sql .= ' t.rowid,';
774 $sql .= " t.doc_date,";
775 $sql .= " t.date_lim_reglement,";
776 $sql .= " t.doc_type,";
777 $sql .= " t.doc_ref,";
778 $sql .= " t.fk_doc,";
779 $sql .= " t.fk_docdet,";
780 $sql .= " t.thirdparty_code,";
781 $sql .= " t.subledger_account,";
782 $sql .= " t.subledger_label,";
783 $sql .= " t.numero_compte,";
784 $sql .= " t.label_compte,";
785 $sql .= " t.label_operation,";
786 $sql .= " t.debit,";
787 $sql .= " t.credit,";
788 $sql .= " t.montant as amount,";
789 $sql .= " t.sens,";
790 $sql .= " t.fk_user_author,";
791 $sql .= " t.import_key,";
792 $sql .= " t.code_journal,";
793 $sql .= " t.journal_label,";
794 $sql .= " t.piece_num,";
795 $sql .= " t.date_creation,";
796 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
797 if ($mode != "_tmp") {
798 $sql .= " t.date_export,";
799 }
800 $sql .= " t.date_validated as date_validation";
801 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.$mode.' as t';
802 $sql .= ' WHERE 1 = 1';
803 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
804 if (null !== $ref) {
805 $sql .= " AND t.rowid = ".((int) $ref);
806 } else {
807 $sql .= " AND t.rowid = ".((int) $id);
808 }
809
810 $resql = $this->db->query($sql);
811 if ($resql) {
812 $numrows = $this->db->num_rows($resql);
813 if ($numrows) {
814 $obj = $this->db->fetch_object($resql);
815
816 $this->id = $obj->rowid;
817
818 $this->doc_date = $this->db->jdate($obj->doc_date);
819 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
820 $this->doc_type = $obj->doc_type;
821 $this->doc_ref = $obj->doc_ref;
822 $this->fk_doc = $obj->fk_doc;
823 $this->fk_docdet = $obj->fk_docdet;
824 $this->thirdparty_code = $obj->thirdparty_code;
825 $this->subledger_account = $obj->subledger_account;
826 $this->subledger_label = $obj->subledger_label;
827 $this->numero_compte = $obj->numero_compte;
828 $this->label_compte = $obj->label_compte;
829 $this->label_operation = $obj->label_operation;
830 $this->debit = $obj->debit;
831 $this->credit = $obj->credit;
832 $this->montant = $obj->amount;
833 $this->amount = $obj->amount;
834 $this->sens = $obj->sens;
835 $this->fk_user_author = $obj->fk_user_author;
836 $this->import_key = $obj->import_key;
837 $this->code_journal = $obj->code_journal;
838 $this->journal_label = $obj->journal_label;
839 $this->piece_num = $obj->piece_num;
840 $this->date_creation = $this->db->jdate($obj->date_creation);
841 $this->date_export = $this->db->jdate($obj->date_export);
842 $this->date_validation = isset($obj->date_validation) ? $this->db->jdate($obj->date_validation) : '';
843 }
844 $this->db->free($resql);
845
846 if ($numrows) {
847 return 1;
848 } else {
849 return 0;
850 }
851 } else {
852 $this->errors[] = 'Error '.$this->db->lasterror();
853 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
854
855 return -1;
856 }
857 }
858
859
873 public function fetchAllByAccount($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0, $countonly = 0)
874 {
875 global $conf;
876
877 dol_syslog(__METHOD__, LOG_DEBUG);
878
879 $this->lines = array();
880 $num = 0;
881
882 $sql = 'SELECT';
883 if ($countonly) {
884 $sql .= ' COUNT(t.rowid) as nb';
885 } else {
886 $sql .= ' t.rowid,';
887 $sql .= " t.doc_date,";
888 $sql .= " t.doc_type,";
889 $sql .= " t.doc_ref,";
890 $sql .= " t.fk_doc,";
891 $sql .= " t.fk_docdet,";
892 $sql .= " t.thirdparty_code,";
893 $sql .= " t.subledger_account,";
894 $sql .= " t.subledger_label,";
895 $sql .= " t.numero_compte,";
896 $sql .= " t.label_compte,";
897 $sql .= " t.label_operation,";
898 $sql .= " t.debit,";
899 $sql .= " t.credit,";
900 $sql .= " t.montant as amount,";
901 $sql .= " t.sens,";
902 $sql .= " t.multicurrency_amount,";
903 $sql .= " t.multicurrency_code,";
904 $sql .= " t.lettering_code,";
905 $sql .= " t.date_lettering,";
906 $sql .= " t.fk_user_author,";
907 $sql .= " t.import_key,";
908 $sql .= " t.code_journal,";
909 $sql .= " t.journal_label,";
910 $sql .= " t.piece_num,";
911 $sql .= " t.date_creation,";
912 $sql .= " t.date_export,";
913 $sql .= " t.date_validated as date_validation,";
914 $sql .= " t.import_key";
915 }
916 // Manage filter
917 $sqlwhere = array();
918 if (count($filter) > 0) {
919 foreach ($filter as $key => $value) {
920 if ($key == 't.doc_date') {
921 $sqlwhere[] = $key.'=\''.$this->db->idate($value).'\'';
922 } elseif ($key == 't.doc_date>=' || $key == 't.doc_date<=') {
923 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
924 } elseif ($key == 't.numero_compte>=' || $key == 't.numero_compte<=' || $key == 't.subledger_account>=' || $key == 't.subledger_account<=') {
925 $sqlwhere[] = $key.'\''.$this->db->escape($value).'\'';
926 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
927 $sqlwhere[] = $key.'='.$value;
928 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
929 $sqlwhere[] = $key.' LIKE \''.$this->db->escape($this->db->escapeforlike($value)).'%\'';
930 } elseif ($key == 't.date_creation>=' || $key == 't.date_creation<=') {
931 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
932 } elseif ($key == 't.date_export>=' || $key == 't.date_export<=') {
933 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
934 } elseif ($key == 't.date_validated>=' || $key == 't.date_validated<=') {
935 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
936 } elseif ($key == 't.credit' || $key == 't.debit') {
937 $sqlwhere[] = natural_search($key, $value, 1, 1);
938 } elseif ($key == 't.reconciled_option') {
939 $sqlwhere[] = 't.lettering_code IS NULL';
940 } elseif ($key == 't.code_journal' && !empty($value)) {
941 if (is_array($value)) {
942 $sqlwhere[] = natural_search("t.code_journal", join(',', $value), 3, 1);
943 } else {
944 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
945 }
946 } elseif ($key == 't.search_accounting_code_in' && !empty($value)) {
947 $sqlwhere[] = 't.numero_compte IN ('.$this->db->sanitize($value, 1).')';
948 } else {
949 $sqlwhere[] = natural_search($key, $value, 0, 1);
950 }
951 }
952 }
953 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
954 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
955 if (count($sqlwhere) > 0) {
956 $sql .= " AND ".implode(" ".$filtermode." ", $sqlwhere);
957 }
958 // Filter by ledger account or subledger account
959 if (!empty($option)) {
960 $sql .= " AND t.subledger_account IS NOT NULL";
961 $sql .= " AND t.subledger_account <> ''";
962 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
963 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
964 } else {
965 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
966 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
967 }
968
969 if (!$countonly) {
970 $sql .= $this->db->order($sortfield, $sortorder);
971 if (!empty($limit)) {
972 $sql .= $this->db->plimit($limit + 1, $offset);
973 }
974 }
975
976 $resql = $this->db->query($sql);
977 if ($resql) {
978 if ($countonly) {
979 $obj = $this->db->fetch_object($resql);
980 if ($obj) {
981 $num = $obj->nb;
982 }
983 } else {
984 $num = $this->db->num_rows($resql);
985
986 $i = 0;
987 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
988 $line = new BookKeepingLine();
989
990 $line->id = $obj->rowid;
991
992 $line->doc_date = $this->db->jdate($obj->doc_date);
993 $line->doc_type = $obj->doc_type;
994 $line->doc_ref = $obj->doc_ref;
995 $line->fk_doc = $obj->fk_doc;
996 $line->fk_docdet = $obj->fk_docdet;
997 $line->thirdparty_code = $obj->thirdparty_code;
998 $line->subledger_account = $obj->subledger_account;
999 $line->subledger_label = $obj->subledger_label;
1000 $line->numero_compte = $obj->numero_compte;
1001 $line->label_compte = $obj->label_compte;
1002 $line->label_operation = $obj->label_operation;
1003 $line->debit = $obj->debit;
1004 $line->credit = $obj->credit;
1005 $line->montant = $obj->amount; // deprecated
1006 $line->amount = $obj->amount;
1007 $line->sens = $obj->sens;
1008 $line->multicurrency_amount = $obj->multicurrency_amount;
1009 $line->multicurrency_code = $obj->multicurrency_code;
1010 $line->lettering_code = $obj->lettering_code;
1011 $line->date_lettering = $obj->date_lettering;
1012 $line->fk_user_author = $obj->fk_user_author;
1013 $line->import_key = $obj->import_key;
1014 $line->code_journal = $obj->code_journal;
1015 $line->journal_label = $obj->journal_label;
1016 $line->piece_num = $obj->piece_num;
1017 $line->date_creation = $this->db->jdate($obj->date_creation);
1018 $line->date_export = $this->db->jdate($obj->date_export);
1019 $line->date_validation = $this->db->jdate($obj->date_validation);
1020 $line->import_key = $obj->import_key;
1021
1022 $this->lines[] = $line;
1023
1024 $i++;
1025 }
1026 }
1027 $this->db->free($resql);
1028
1029 return $num;
1030 } else {
1031 $this->errors[] = 'Error '.$this->db->lasterror();
1032 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1033
1034 return -1;
1035 }
1036 }
1037
1050 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $showAlreadyExportMovements = 1)
1051 {
1052 global $conf;
1053
1054 dol_syslog(__METHOD__, LOG_DEBUG);
1055
1056 $sql = 'SELECT';
1057 $sql .= ' t.rowid,';
1058 $sql .= " t.doc_date,";
1059 $sql .= " t.doc_type,";
1060 $sql .= " t.doc_ref,";
1061 $sql .= " t.fk_doc,";
1062 $sql .= " t.fk_docdet,";
1063 $sql .= " t.thirdparty_code,";
1064 $sql .= " t.subledger_account,";
1065 $sql .= " t.subledger_label,";
1066 $sql .= " t.numero_compte,";
1067 $sql .= " t.label_compte,";
1068 $sql .= " t.label_operation,";
1069 $sql .= " t.debit,";
1070 $sql .= " t.credit,";
1071 $sql .= " t.lettering_code,";
1072 $sql .= " t.date_lettering,";
1073 $sql .= " t.montant as amount,";
1074 $sql .= " t.sens,";
1075 $sql .= " t.fk_user_author,";
1076 $sql .= " t.import_key,";
1077 $sql .= " t.code_journal,";
1078 $sql .= " t.journal_label,";
1079 $sql .= " t.piece_num,";
1080 $sql .= " t.date_creation,";
1081 $sql .= " t.date_lim_reglement,";
1082 $sql .= " t.tms as date_modification,";
1083 $sql .= " t.date_export,";
1084 $sql .= " t.date_validated as date_validation";
1085 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1086 // Manage filter
1087 $sqlwhere = array();
1088 if (count($filter) > 0) {
1089 foreach ($filter as $key => $value) {
1090 if ($key == 't.doc_date') {
1091 $sqlwhere[] = $key.'=\''.$this->db->idate($value).'\'';
1092 } elseif ($key == 't.doc_date>=' || $key == 't.doc_date<=') {
1093 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
1094 } elseif ($key == 't.numero_compte>=' || $key == 't.numero_compte<=' || $key == 't.subledger_account>=' || $key == 't.subledger_account<=') {
1095 $sqlwhere[] = $key.'\''.$this->db->escape($value).'\'';
1096 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1097 $sqlwhere[] = $key.'='.((int) $value);
1098 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1099 $sqlwhere[] = $key.' LIKE \''.$this->db->escape($value).'%\'';
1100 } elseif ($key == 't.date_creation>=' || $key == 't.date_creation<=') {
1101 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
1102 } elseif ($key == 't.tms>=' || $key == 't.tms<=') {
1103 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
1104 } elseif ($key == 't.date_export>=' || $key == 't.date_export<=') {
1105 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
1106 } elseif ($key == 't.date_validated>=' || $key == 't.date_validated<=') {
1107 $sqlwhere[] = $key.'\''.$this->db->idate($value).'\'';
1108 } elseif ($key == 't.credit' || $key == 't.debit') {
1109 $sqlwhere[] = natural_search($key, $value, 1, 1);
1110 } elseif ($key == 't.code_journal' && !empty($value)) {
1111 if (is_array($value)) {
1112 $sqlwhere[] = natural_search("t.code_journal", join(',', $value), 3, 1);
1113 } else {
1114 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1115 }
1116 } else {
1117 $sqlwhere[] = natural_search($key, $value, 0, 1);
1118 }
1119 }
1120 }
1121 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1122 if ($showAlreadyExportMovements == 0) {
1123 $sql .= " AND t.date_export IS NULL";
1124 }
1125 if (count($sqlwhere) > 0) {
1126 $sql .= ' AND '.implode(" ".$filtermode." ", $sqlwhere);
1127 }
1128 if (!empty($sortfield)) {
1129 $sql .= $this->db->order($sortfield, $sortorder);
1130 }
1131 if (!empty($limit)) {
1132 $sql .= $this->db->plimit($limit + 1, $offset);
1133 }
1134 $this->lines = array();
1135
1136 $resql = $this->db->query($sql);
1137 if ($resql) {
1138 $num = $this->db->num_rows($resql);
1139
1140 $i = 0;
1141 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1142 $line = new BookKeepingLine();
1143
1144 $line->id = $obj->rowid;
1145
1146 $line->doc_date = $this->db->jdate($obj->doc_date);
1147 $line->doc_type = $obj->doc_type;
1148 $line->doc_ref = $obj->doc_ref;
1149 $line->fk_doc = $obj->fk_doc;
1150 $line->fk_docdet = $obj->fk_docdet;
1151 $line->thirdparty_code = $obj->thirdparty_code;
1152 $line->subledger_account = $obj->subledger_account;
1153 $line->subledger_label = $obj->subledger_label;
1154 $line->numero_compte = $obj->numero_compte;
1155 $line->label_compte = $obj->label_compte;
1156 $line->label_operation = $obj->label_operation;
1157 $line->debit = $obj->debit;
1158 $line->credit = $obj->credit;
1159 $line->montant = $obj->amount; // deprecated
1160 $line->amount = $obj->amount;
1161 $line->sens = $obj->sens;
1162 $line->lettering_code = $obj->lettering_code;
1163 $line->date_lettering = $obj->date_lettering;
1164 $line->fk_user_author = $obj->fk_user_author;
1165 $line->import_key = $obj->import_key;
1166 $line->code_journal = $obj->code_journal;
1167 $line->journal_label = $obj->journal_label;
1168 $line->piece_num = $obj->piece_num;
1169 $line->date_creation = $this->db->jdate($obj->date_creation);
1170 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1171 $line->date_modification = $this->db->jdate($obj->date_modification);
1172 $line->date_export = $this->db->jdate($obj->date_export);
1173 $line->date_validation = $this->db->jdate($obj->date_validation);
1174
1175 $this->lines[] = $line;
1176
1177 $i++;
1178 }
1179 $this->db->free($resql);
1180
1181 return $num;
1182 } else {
1183 $this->errors[] = 'Error '.$this->db->lasterror();
1184 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1185 return -1;
1186 }
1187 }
1188
1201 public function fetchAllBalance($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0)
1202 {
1203 global $conf;
1204
1205 $this->lines = array();
1206
1207 dol_syslog(__METHOD__, LOG_DEBUG);
1208
1209 $sql = 'SELECT';
1210 $sql .= " t.numero_compte,";
1211 if (!empty($option)) {
1212 $sql .= " t.subledger_account,";
1213 $sql .= " t.subledger_label,";
1214 }
1215 $sql .= " SUM(t.debit) as debit,";
1216 $sql .= " SUM(t.credit) as credit";
1217 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1218 // Manage filter
1219 $sqlwhere = array();
1220 if (count($filter) > 0) {
1221 foreach ($filter as $key => $value) {
1222 if ($key == 't.doc_date') {
1223 $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
1224 } elseif ($key == 't.doc_date>=' || $key == 't.doc_date<=' || $key == 't.doc_date>' || $key == 't.doc_date<') {
1225 $sqlwhere[] = $key."'".$this->db->idate($value)."'";
1226 } elseif ($key == 't.numero_compte>=' || $key == 't.numero_compte<=' || $key == 't.subledger_account>=' || $key == 't.subledger_account<=') {
1227 $sqlwhere[] = $key."'".$this->db->escape($value)."'";
1228 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1229 $sqlwhere[] = $key." = ".((int) $value);
1230 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1231 $sqlwhere[] = $key." LIKE '".$this->db->escape($value)."%'";
1232 } elseif ($key == 't.subledger_label') {
1233 $sqlwhere[] = $key." LIKE '".$this->db->escape($value)."%'";
1234 } elseif ($key == 't.code_journal' && !empty($value)) {
1235 if (is_array($value)) {
1236 $sqlwhere[] = natural_search("t.code_journal", join(',', $value), 3, 1);
1237 } else {
1238 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1239 }
1240 } elseif ($key == 't.reconciled_option') {
1241 $sqlwhere[] = 't.lettering_code IS NULL';
1242 } else {
1243 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
1244 }
1245 }
1246 }
1247 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1248 if (count($sqlwhere) > 0) {
1249 $sql .= " AND ".implode(" ".$filtermode." ", $sqlwhere);
1250 }
1251
1252 if (!empty($option)) {
1253 $sql .= " AND t.subledger_account IS NOT NULL";
1254 $sql .= " AND t.subledger_account <> ''";
1255 $sql .= " GROUP BY t.numero_compte, t.subledger_account, t.subledger_label";
1256 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
1257 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
1258 } else {
1259 $sql .= ' GROUP BY t.numero_compte';
1260 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
1261 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
1262 }
1263
1264 if (!empty($sortfield)) {
1265 $sql .= $this->db->order($sortfield, $sortorder);
1266 }
1267 if (!empty($limit)) {
1268 $sql .= $this->db->plimit($limit + 1, $offset);
1269 }
1270
1271 $resql = $this->db->query($sql);
1272 if ($resql) {
1273 $num = $this->db->num_rows($resql);
1274
1275 $i = 0;
1276 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1277 $line = new BookKeepingLine();
1278
1279 $line->numero_compte = $obj->numero_compte;
1280 //$line->label_compte = $obj->label_compte;
1281 if (!empty($option)) {
1282 $line->subledger_account = $obj->subledger_account;
1283 $line->subledger_label = $obj->subledger_label;
1284 }
1285 $line->debit = $obj->debit;
1286 $line->credit = $obj->credit;
1287
1288 $this->lines[] = $line;
1289
1290 $i++;
1291 }
1292 $this->db->free($resql);
1293
1294 return $num;
1295 } else {
1296 $this->errors[] = 'Error '.$this->db->lasterror();
1297 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1298
1299 return -1;
1300 }
1301 }
1302
1311 public function update(User $user, $notrigger = false, $mode = '')
1312 {
1313 global $langs;
1314 $error = 0;
1315
1316 dol_syslog(__METHOD__, LOG_DEBUG);
1317
1318 // Clean parameters
1319 if (isset($this->doc_type)) {
1320 $this->doc_type = trim($this->doc_type);
1321 }
1322 if (isset($this->doc_ref)) {
1323 $this->doc_ref = trim($this->doc_ref);
1324 }
1325 if (isset($this->fk_doc)) {
1326 $this->fk_doc = (int) $this->fk_doc;
1327 }
1328 if (isset($this->fk_docdet)) {
1329 $this->fk_docdet = (int) $this->fk_docdet;
1330 }
1331 if (isset($this->thirdparty_code)) {
1332 $this->thirdparty_code = trim($this->thirdparty_code);
1333 }
1334 if (isset($this->subledger_account)) {
1335 $this->subledger_account = trim($this->subledger_account);
1336 }
1337 if (isset($this->subledger_label)) {
1338 $this->subledger_label = trim($this->subledger_label);
1339 }
1340 if (isset($this->numero_compte)) {
1341 $this->numero_compte = trim($this->numero_compte);
1342 }
1343 if (isset($this->label_compte)) {
1344 $this->label_compte = trim($this->label_compte);
1345 }
1346 if (isset($this->label_operation)) {
1347 $this->label_operation = trim($this->label_operation);
1348 }
1349 if (isset($this->debit)) {
1350 $this->debit = trim($this->debit);
1351 }
1352 if (isset($this->credit)) {
1353 $this->credit = trim($this->credit);
1354 }
1355 if (isset($this->amount)) {
1356 $this->amount = trim($this->amount);
1357 }
1358 if (isset($this->sens)) {
1359 $this->sens = trim($this->sens);
1360 }
1361 if (isset($this->import_key)) {
1362 $this->import_key = trim($this->import_key);
1363 }
1364 if (isset($this->code_journal)) {
1365 $this->code_journal = trim($this->code_journal);
1366 }
1367 if (isset($this->journal_label)) {
1368 $this->journal_label = trim($this->journal_label);
1369 }
1370 if (isset($this->piece_num)) {
1371 $this->piece_num = trim($this->piece_num);
1372 }
1373
1374 $result = $this->canModifyBookkeeping($this->id);
1375 if ($result < 0) {
1376 return -1;
1377 } elseif ($result == 0) {
1378 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1379 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1380 } else {
1381 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1382 }
1383 return -1;
1384 }
1385
1386 $this->debit = price2num($this->debit, 'MT');
1387 $this->credit = price2num($this->credit, 'MT');
1388
1389 // Check parameters
1390 // Put here code to add a control on parameters values
1391
1392 // Update request
1393 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.$mode.' SET';
1394 $sql .= ' doc_date = '.(!isset($this->doc_date) || dol_strlen($this->doc_date) != 0 ? "'".$this->db->idate($this->doc_date)."'" : 'null').',';
1395 $sql .= ' doc_type = '.(isset($this->doc_type) ? "'".$this->db->escape($this->doc_type)."'" : "null").',';
1396 $sql .= ' doc_ref = '.(isset($this->doc_ref) ? "'".$this->db->escape($this->doc_ref)."'" : "null").',';
1397 $sql .= ' fk_doc = '.(isset($this->fk_doc) ? $this->fk_doc : "null").',';
1398 $sql .= ' fk_docdet = '.(isset($this->fk_docdet) ? $this->fk_docdet : "null").',';
1399 $sql .= ' thirdparty_code = '.(isset($this->thirdparty_code) ? "'".$this->db->escape($this->thirdparty_code)."'" : "null").',';
1400 $sql .= ' subledger_account = '.(isset($this->subledger_account) ? "'".$this->db->escape($this->subledger_account)."'" : "null").',';
1401 $sql .= ' subledger_label = '.(isset($this->subledger_label) ? "'".$this->db->escape($this->subledger_label)."'" : "null").',';
1402 $sql .= ' numero_compte = '.(isset($this->numero_compte) ? "'".$this->db->escape($this->numero_compte)."'" : "null").',';
1403 $sql .= ' label_compte = '.(isset($this->label_compte) ? "'".$this->db->escape($this->label_compte)."'" : "null").',';
1404 $sql .= ' label_operation = '.(isset($this->label_operation) ? "'".$this->db->escape($this->label_operation)."'" : "null").',';
1405 $sql .= ' debit = '.(isset($this->debit) ? $this->debit : "null").',';
1406 $sql .= ' credit = '.(isset($this->credit) ? $this->credit : "null").',';
1407 $sql .= ' montant = '.(isset($this->montant) ? $this->montant : "null").',';
1408 $sql .= ' sens = '.(isset($this->sens) ? "'".$this->db->escape($this->sens)."'" : "null").',';
1409 $sql .= ' fk_user_author = '.(isset($this->fk_user_author) ? $this->fk_user_author : "null").',';
1410 $sql .= ' import_key = '.(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").',';
1411 $sql .= ' code_journal = '.(isset($this->code_journal) ? "'".$this->db->escape($this->code_journal)."'" : "null").',';
1412 $sql .= ' journal_label = '.(isset($this->journal_label) ? "'".$this->db->escape($this->journal_label)."'" : "null").',';
1413 $sql .= ' piece_num = '.(isset($this->piece_num) ? $this->piece_num : "null");
1414 $sql .= ' WHERE rowid='.((int) $this->id);
1415
1416 $this->db->begin();
1417
1418 $resql = $this->db->query($sql);
1419 if (!$resql) {
1420 $error++;
1421 $this->errors[] = 'Error '.$this->db->lasterror();
1422 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1423 }
1424
1425 // Uncomment this and change MYOBJECT to your own tag if you
1426 // want this action calls a trigger.
1427 //if (! $error && ! $notrigger) {
1428
1429 // // Call triggers
1430 // $result=$this->call_trigger('MYOBJECT_MODIFY',$user);
1431 // if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
1432 // // End call triggers
1433 //}
1434
1435 // Commit or rollback
1436 if ($error) {
1437 $this->db->rollback();
1438
1439 return -1 * $error;
1440 } else {
1441 $this->db->commit();
1442
1443 return 1;
1444 }
1445 }
1446
1456 public function updateByMvt($piece_num = '', $field = '', $value = '', $mode = '')
1457 {
1458 $error = 0;
1459
1460 $sql_filter = $this->getCanModifyBookkeepingSQL();
1461 if (!isset($sql_filter)) {
1462 return -1;
1463 }
1464
1465 $this->db->begin();
1466
1467 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element.$mode;
1468 $sql .= " SET ".$field." = ".(is_numeric($value) ? ((float) $value) : "'".$this->db->escape($value)."'");
1469 $sql .= " WHERE piece_num = ".((int) $piece_num);
1470 $sql .= $sql_filter;
1471
1472 $resql = $this->db->query($sql);
1473
1474 if (!$resql) {
1475 $error++;
1476 $this->errors[] = 'Error '.$this->db->lasterror();
1477 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1478 }
1479 if ($error) {
1480 $this->db->rollback();
1481
1482 return -1 * $error;
1483 } else {
1484 $this->db->commit();
1485
1486 return 1;
1487 }
1488 }
1489
1498 public function delete(User $user, $notrigger = 0, $mode = '')
1499 {
1500 global $langs;
1501
1502 dol_syslog(__METHOD__, LOG_DEBUG);
1503
1504 $result = $this->canModifyBookkeeping($this->id, $mode);
1505 if ($result < 0) {
1506 return -1;
1507 } elseif ($result == 0) {
1508 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1509 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1510 } else {
1511 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1512 }
1513 return -1;
1514 }
1515
1516 $error = 0;
1517
1518 $this->db->begin();
1519
1520 // Uncomment this and change MYOBJECT to your own tag if you
1521 // want this action calls a trigger.
1522 //if (! $error && ! $notrigger) {
1523
1524 // // Call triggers
1525 // $result=$this->call_trigger('MYOBJECT_DELETE',$user);
1526 // if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
1527 // // End call triggers
1528 //}
1529
1530 if (!$error) {
1531 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.$mode;
1532 $sql .= ' WHERE rowid='.((int) $this->id);
1533
1534 $resql = $this->db->query($sql);
1535 if (!$resql) {
1536 $error++;
1537 $this->errors[] = 'Error '.$this->db->lasterror();
1538 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1539 }
1540 }
1541
1542 // Commit or rollback
1543 if ($error) {
1544 $this->db->rollback();
1545
1546 return -1 * $error;
1547 } else {
1548 $this->db->commit();
1549
1550 return 1;
1551 }
1552 }
1553
1561 public function deleteByImportkey($importkey, $mode = '')
1562 {
1563 $this->db->begin();
1564
1565 $sql_filter = $this->getCanModifyBookkeepingSQL();
1566 if (!isset($sql_filter)) {
1567 return -1;
1568 }
1569
1570 // first check if line not yet in bookkeeping
1571 $sql = "DELETE";
1572 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1573 $sql .= " WHERE import_key = '".$this->db->escape($importkey)."'";
1574 $sql .= $sql_filter;
1575
1576 $resql = $this->db->query($sql);
1577
1578 if (!$resql) {
1579 $this->errors[] = "Error ".$this->db->lasterror();
1580 dol_syslog(get_class($this)."::delete Error ".$this->db->lasterror(), LOG_ERR);
1581 $this->db->rollback();
1582 return -1;
1583 }
1584
1585 $this->db->commit();
1586 return 1;
1587 }
1588
1598 public function deleteByYearAndJournal($delyear = 0, $journal = '', $mode = '', $delmonth = 0)
1599 {
1600 global $conf, $langs;
1601
1602 if (empty($delyear) && empty($journal)) {
1603 $this->error = 'ErrorOneFieldRequired';
1604 return -1;
1605 }
1606 if (!empty($delmonth) && empty($delyear)) {
1607 $this->error = 'YearRequiredIfMonthDefined';
1608 return -2;
1609 }
1610
1611 $sql_filter = $this->getCanModifyBookkeepingSQL();
1612 if (!isset($sql_filter)) {
1613 return -1;
1614 }
1615
1616 $this->db->begin();
1617
1618 // Delete record in bookkeeping
1619 $sql = "DELETE";
1620 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1621 $sql .= " WHERE 1 = 1";
1622 $sql .= dolSqlDateFilter('doc_date', 0, $delmonth, $delyear);
1623 if (!empty($journal)) {
1624 $sql .= " AND code_journal = '".$this->db->escape($journal)."'";
1625 }
1626 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1627 // Exclusion of validated entries at the time of deletion
1628 $sql .= " AND date_validated IS NULL";
1629 $sql .= $sql_filter;
1630
1631 // TODO: In a future we must forbid deletion if record is inside a closed fiscal period.
1632
1633 $resql = $this->db->query($sql);
1634
1635 if (!$resql) {
1636 $this->errors[] = "Error ".$this->db->lasterror();
1637 foreach ($this->errors as $errmsg) {
1638 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1639 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1640 }
1641 $this->db->rollback();
1642 return -1;
1643 }
1644
1645 $this->db->commit();
1646 return 1;
1647 }
1648
1656 public function deleteMvtNum($piecenum, $mode = '')
1657 {
1658 global $conf;
1659
1660 $sql_filter = $this->getCanModifyBookkeepingSQL();
1661 if (!isset($sql_filter)) {
1662 return -1;
1663 }
1664
1665 $this->db->begin();
1666
1667 // first check if line not yet in bookkeeping
1668 $sql = "DELETE";
1669 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1670 $sql .= " WHERE piece_num = ".(int) $piecenum;
1671 $sql .= " AND date_validated IS NULL"; // For security, exclusion of validated entries at the time of deletion
1672 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1673 $sql .= $sql_filter;
1674
1675 $resql = $this->db->query($sql);
1676
1677 if (!$resql) {
1678 $this->errors[] = "Error ".$this->db->lasterror();
1679 foreach ($this->errors as $errmsg) {
1680 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1681 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1682 }
1683 $this->db->rollback();
1684 return -1;
1685 }
1686
1687 $this->db->commit();
1688 return 1;
1689 }
1690
1698 public function createFromClone(User $user, $fromid)
1699 {
1700 dol_syslog(__METHOD__, LOG_DEBUG);
1701
1702 $error = 0;
1703 $object = new BookKeeping($this->db);
1704
1705 $this->db->begin();
1706
1707 // Load source object
1708 $object->fetch($fromid);
1709 // Reset object
1710 $object->id = 0;
1711
1712 // Clear fields
1713 // ...
1714
1715 // Create clone
1716 $object->context['createfromclone'] = 'createfromclone';
1717 $result = $object->create($user);
1718
1719 // Other options
1720 if ($result < 0) {
1721 $error++;
1722 $this->errors = $object->errors;
1723 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1724 }
1725
1726 unset($object->context['createfromclone']);
1727
1728 // End
1729 if (!$error) {
1730 $this->db->commit();
1731
1732 return $object->id;
1733 } else {
1734 $this->db->rollback();
1735
1736 return -1;
1737 }
1738 }
1739
1746 public function initAsSpecimen()
1747 {
1748 global $user;
1749
1750 $now = dol_now();
1751
1752 $this->id = 0;
1753 $this->doc_date = $now;
1754 $this->doc_type = '';
1755 $this->doc_ref = '';
1756 $this->fk_doc = 0;
1757 $this->fk_docdet = 0;
1758 $this->thirdparty_code = 'CU001';
1759 $this->subledger_account = '41100001';
1760 $this->subledger_label = 'My customer company';
1761 $this->numero_compte = '411';
1762 $this->label_compte = 'Customer';
1763 $this->label_operation = 'Sales of pea';
1764 $this->debit = 99.9;
1765 $this->credit = 0.0;
1766 $this->amount = 0.0;
1767 $this->sens = 'D';
1768 $this->fk_user_author = $user->id;
1769 $this->import_key = '20201027';
1770 $this->code_journal = 'VT';
1771 $this->journal_label = 'Journal de vente';
1772 $this->piece_num = 1234;
1773 $this->date_creation = $now;
1774 }
1775
1783 public function fetchPerMvt($piecenum, $mode = '')
1784 {
1785 global $conf;
1786
1787 $sql = "SELECT piece_num, doc_date,code_journal, journal_label, doc_ref, doc_type,";
1788 $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1789 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1790 if ($mode != "_tmp") {
1791 $sql .= ", date_export";
1792 }
1793 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1794 $sql .= " WHERE piece_num = ".((int) $piecenum);
1795 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1796
1797 dol_syslog(__METHOD__, LOG_DEBUG);
1798 $result = $this->db->query($sql);
1799 if ($result) {
1800 $obj = $this->db->fetch_object($result);
1801
1802 $this->piece_num = $obj->piece_num;
1803 $this->code_journal = $obj->code_journal;
1804 $this->journal_label = $obj->journal_label;
1805 $this->doc_date = $this->db->jdate($obj->doc_date);
1806 $this->doc_ref = $obj->doc_ref;
1807 $this->doc_type = $obj->doc_type;
1808 $this->date_creation = $this->db->jdate($obj->date_creation);
1809 $this->date_modification = $this->db->jdate($obj->date_modification);
1810 if ($mode != "_tmp") {
1811 $this->date_export = $this->db->jdate($obj->date_export);
1812 }
1813 $this->date_validation = $this->db->jdate($obj->date_validation);
1814 } else {
1815 $this->error = "Error ".$this->db->lasterror();
1816 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1817 return -1;
1818 }
1819
1820 return 1;
1821 }
1822
1829 public function getNextNumMvt($mode = '')
1830 {
1831 global $conf;
1832
1833 $sql = "SELECT MAX(piece_num)+1 as max FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1834 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1835
1836 dol_syslog(get_class($this)."::getNextNumMvt", LOG_DEBUG);
1837
1838 $result = $this->db->query($sql);
1839
1840 if ($result) {
1841 $obj = $this->db->fetch_object($result);
1842 if ($obj) {
1843 $result = $obj->max;
1844 }
1845 if (empty($result)) {
1846 $result = 1;
1847 }
1848 return $result;
1849 } else {
1850 $this->error = "Error ".$this->db->lasterror();
1851 dol_syslog(get_class($this)."::getNextNumMvt ".$this->error, LOG_ERR);
1852 return -1;
1853 }
1854 }
1855
1863 public function fetchAllPerMvt($piecenum, $mode = '')
1864 {
1865 global $conf;
1866
1867 $sql = "SELECT rowid, doc_date, doc_type,";
1868 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1869 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1870 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
1871 $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1872 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1873 if ($mode != "_tmp") {
1874 $sql .= ", date_export";
1875 }
1876 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element.$mode;
1877 $sql .= " WHERE piece_num = ".((int) $piecenum);
1878 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1879
1880 dol_syslog(__METHOD__, LOG_DEBUG);
1881 $result = $this->db->query($sql);
1882 if ($result) {
1883 while ($obj = $this->db->fetch_object($result)) {
1884 $line = new BookKeepingLine();
1885
1886 $line->id = $obj->rowid;
1887
1888 $line->doc_date = $this->db->jdate($obj->doc_date);
1889 $line->doc_type = $obj->doc_type;
1890 $line->doc_ref = $obj->doc_ref;
1891 $line->fk_doc = $obj->fk_doc;
1892 $line->fk_docdet = $obj->fk_docdet;
1893 $line->thirdparty_code = $obj->thirdparty_code;
1894 $line->subledger_account = $obj->subledger_account;
1895 $line->subledger_label = $obj->subledger_label;
1896 $line->numero_compte = $obj->numero_compte;
1897 $line->label_compte = $obj->label_compte;
1898 $line->label_operation = $obj->label_operation;
1899 $line->debit = $obj->debit;
1900 $line->credit = $obj->credit;
1901 $line->montant = $obj->amount;
1902 $line->amount = $obj->amount;
1903 $line->sens = $obj->sens;
1904 $line->code_journal = $obj->code_journal;
1905 $line->journal_label = $obj->journal_label;
1906 $line->piece_num = $obj->piece_num;
1907 $line->date_creation = $obj->date_creation;
1908 $line->date_modification = $obj->date_modification;
1909 if ($mode != "_tmp") {
1910 $line->date_export = $obj->date_export;
1911 }
1912 $line->date_validation = $obj->date_validation;
1913
1914 $this->linesmvt[] = $line;
1915 }
1916 } else {
1917 $this->error = "Error ".$this->db->lasterror();
1918 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1919 return -1;
1920 }
1921
1922 return 1;
1923 }
1924
1925 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1932 public function export_bookkeeping($model = 'ebp')
1933 {
1934 // phpcs:enable
1935 global $conf;
1936
1937 $sql = "SELECT rowid, doc_date, doc_type,";
1938 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1939 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1940 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, piece_num,";
1941 $sql .= " date_validated as date_validation";
1942 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
1943 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1944
1945 dol_syslog(get_class($this)."::export_bookkeeping", LOG_DEBUG);
1946
1947 $resql = $this->db->query($sql);
1948
1949 if ($resql) {
1950 $this->linesexport = array();
1951
1952 $num = $this->db->num_rows($resql);
1953 while ($obj = $this->db->fetch_object($resql)) {
1954 $line = new BookKeepingLine();
1955
1956 $line->id = $obj->rowid;
1957
1958 $line->doc_date = $this->db->jdate($obj->doc_date);
1959 $line->doc_type = $obj->doc_type;
1960 $line->doc_ref = $obj->doc_ref;
1961 $line->fk_doc = $obj->fk_doc;
1962 $line->fk_docdet = $obj->fk_docdet;
1963 $line->thirdparty_code = $obj->thirdparty_code;
1964 $line->subledger_account = $obj->subledger_account;
1965 $line->subledger_label = $obj->subledger_label;
1966 $line->numero_compte = $obj->numero_compte;
1967 $line->label_compte = $obj->label_compte;
1968 $line->label_operation = $obj->label_operation;
1969 $line->debit = $obj->debit;
1970 $line->credit = $obj->credit;
1971 $line->montant = $obj->amount;
1972 $line->amount = $obj->amount;
1973 $line->sens = $obj->sens;
1974 $line->code_journal = $obj->code_journal;
1975 $line->piece_num = $obj->piece_num;
1976 $line->date_validation = $obj->date_validation;
1977
1978 $this->linesexport[] = $line;
1979 }
1980 $this->db->free($resql);
1981
1982 return $num;
1983 } else {
1984 $this->error = "Error ".$this->db->lasterror();
1985 dol_syslog(get_class($this)."::export_bookkeeping ".$this->error, LOG_ERR);
1986 return -1;
1987 }
1988 }
1989
1997 public function transformTransaction($direction = 0, $piece_num = '')
1998 {
1999 global $conf;
2000
2001 $error = 0;
2002
2003 $sql_filter = $this->getCanModifyBookkeepingSQL();
2004 if (!isset($sql_filter)) {
2005 return -1;
2006 }
2007
2008 $this->db->begin();
2009
2010 if ($direction == 0) {
2011 $next_piecenum = $this->getNextNumMvt();
2012 $now = dol_now();
2013
2014 if ($next_piecenum < 0) {
2015 $error++;
2016 }
2017
2018 if (!$error) {
2019 // Delete if there is an empty line
2020 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity)." AND numero_compte IS NULL AND debit = 0 AND credit = 0";
2021 $resql = $this->db->query($sql);
2022 if (!$resql) {
2023 $error++;
2024 $this->errors[] = 'Error '.$this->db->lasterror();
2025 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2026 }
2027 }
2028
2029 if (!$error) {
2030 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.' (doc_date, doc_type,';
2031 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2032 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2033 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num, date_creation)';
2034 $sql .= ' SELECT doc_date, doc_type,';
2035 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2036 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2037 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, '.((int) $next_piecenum).", '".$this->db->idate($now)."'";
2038 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND numero_compte IS NOT NULL AND entity = ' .((int) $conf->entity);
2039 $sql .= $sql_filter;
2040 $resql = $this->db->query($sql);
2041 if (!$resql) {
2042 $error++;
2043 $this->errors[] = 'Error '.$this->db->lasterror();
2044 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2045 }
2046 }
2047
2048 if (!$error) {
2049 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2050 $resql = $this->db->query($sql);
2051 if (!$resql) {
2052 $error++;
2053 $this->errors[] = 'Error '.$this->db->lasterror();
2054 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2055 }
2056 }
2057 } elseif ($direction == 1) {
2058 if (!$error) {
2059 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2060 $resql = $this->db->query($sql);
2061 if (!$resql) {
2062 $error++;
2063 $this->errors[] = 'Error '.$this->db->lasterror();
2064 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2065 }
2066 }
2067
2068 if (!$error) {
2069 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'_tmp (doc_date, doc_type,';
2070 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2071 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2072 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num)';
2073 $sql .= ' SELECT doc_date, doc_type,';
2074 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2075 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2076 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num';
2077 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2078 $sql .= $sql_filter;
2079 $resql = $this->db->query($sql);
2080 if (!$resql) {
2081 $error++;
2082 $this->errors[] = 'Error '.$this->db->lasterror();
2083 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2084 }
2085 }
2086
2087 if (!$error) {
2088 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2089 $sql .= $sql_filter;
2090 $resql = $this->db->query($sql);
2091 if (!$resql) {
2092 $error++;
2093 $this->errors[] = 'Error '.$this->db->lasterror();
2094 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
2095 }
2096 }
2097 }
2098 if (!$error) {
2099 $this->db->commit();
2100 return 1;
2101 } else {
2102 $this->db->rollback();
2103 return -1;
2104 }
2105 /*
2106 $sql = "DELETE FROM ";
2107 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as ab";
2108 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2109 $sql .= " AND aa.active = 1";
2110 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2111 $sql .= " AND asy.rowid = " . ((int) $pcgver);
2112 $sql .= " AND ab.entity IN (" . getEntity('accountancy') . ")";
2113 $sql .= " ORDER BY account_number ASC";
2114 */
2115 }
2116
2117 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2130 public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $aabase = '')
2131 {
2132 // phpcs:enable
2133 global $conf;
2134
2135 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
2136
2137 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2138
2139 $sql = "SELECT DISTINCT ab.numero_compte as account_number, aa.label as label, aa.rowid as rowid, aa.fk_pcg_version";
2140 $sql .= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab";
2141 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON aa.account_number = ab.numero_compte";
2142 $sql .= " AND aa.active = 1";
2143 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2144 $sql .= " AND asy.rowid = ".((int) $pcgver);
2145 $sql .= " AND ab.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2146 $sql .= " ORDER BY account_number ASC";
2147
2148 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2149 $resql = $this->db->query($sql);
2150
2151 if (!$resql) {
2152 $this->error = "Error ".$this->db->lasterror();
2153 dol_syslog(get_class($this)."::select_account ".$this->error, LOG_ERR);
2154 return -1;
2155 }
2156
2157 $out = ajax_combobox($htmlname, $event);
2158
2159 $options = array();
2160 $selected = null;
2161
2162 while ($obj = $this->db->fetch_object($resql)) {
2163 $label = length_accountg($obj->account_number).' - '.$obj->label;
2164
2165 $select_value_in = $obj->rowid;
2166 $select_value_out = $obj->rowid;
2167
2168 if ($select_in == 1) {
2169 $select_value_in = $obj->account_number;
2170 }
2171 if ($select_out == 1) {
2172 $select_value_out = $obj->account_number;
2173 }
2174
2175 // Remember guy's we store in database llx_facturedet the rowid of accounting_account and not the account_number
2176 // Because same account_number can be share between different accounting_system and do have the same meaning
2177 if (($selectid != '') && $selectid == $select_value_in) {
2178 $selected = $select_value_out;
2179 }
2180
2181 $options[$select_value_out] = $label;
2182 }
2183
2184 $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300');
2185 $this->db->free($resql);
2186 return $out;
2187 }
2188
2196 public function getRootAccount($account = null)
2197 {
2198 global $conf;
2199 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2200
2201 $sql = "SELECT root.rowid, root.account_number, root.label as label,";
2202 $sql .= " parent.rowid as parent_rowid, parent.account_number as parent_account_number, parent.label as parent_label";
2203 $sql .= " FROM ".MAIN_DB_PREFIX."accounting_account as aa";
2204 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2205 $sql .= " AND asy.rowid = ".((int) $pcgver);
2206 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as parent ON aa.account_parent = parent.rowid AND parent.active = 1";
2207 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as root ON parent.account_parent = root.rowid AND root.active = 1";
2208 $sql .= " WHERE aa.account_number = '".$this->db->escape($account)."'";
2209 $sql .= " AND aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2210
2211 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2212 $resql = $this->db->query($sql);
2213 if ($resql) {
2214 $obj = '';
2215 if ($this->db->num_rows($resql)) {
2216 $obj = $this->db->fetch_object($resql);
2217 }
2218
2219 $result = array('id'=>$obj->rowid, 'account_number'=>$obj->account_number, 'label'=>$obj->label);
2220 return $result;
2221 } else {
2222 $this->error = "Error ".$this->db->lasterror();
2223 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2224
2225 return -1;
2226 }
2227 }
2228
2229 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2236 public function get_compte_desc($account = null)
2237 {
2238 // phpcs:enable
2239 global $conf;
2240
2241 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2242 $sql = "SELECT aa.account_number, aa.label, aa.rowid, aa.fk_pcg_version, cat.label as category";
2243 $sql .= " FROM ".MAIN_DB_PREFIX."accounting_account as aa ";
2244 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2245 $sql .= " AND aa.account_number = '".$this->db->escape($account)."'";
2246 $sql .= " AND asy.rowid = ".((int) $pcgver);
2247 $sql .= " AND aa.active = 1";
2248 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_accounting_category as cat ON aa.fk_accounting_category = cat.rowid";
2249 $sql .= " WHERE aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2250
2251 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2252 $resql = $this->db->query($sql);
2253 if ($resql) {
2254 $obj = '';
2255 if ($this->db->num_rows($resql)) {
2256 $obj = $this->db->fetch_object($resql);
2257 }
2258 if (empty($obj->category)) {
2259 return $obj->label;
2260 } else {
2261 return $obj->label.' ('.$obj->category.')';
2262 }
2263 } else {
2264 $this->error = "Error ".$this->db->lasterror();
2265 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2266 return -1;
2267 }
2268 }
2269
2277 public function getCanModifyBookkeepingSQL($alias = '', $force = false)
2278 {
2279 global $conf;
2280
2281 $alias = trim($alias);
2282 $alias = !empty($alias) && strpos($alias, '.') < 0 ? $alias . "." : $alias;
2283
2284 if (!isset(self::$can_modify_bookkeeping_sql_cached[$alias]) || $force) {
2285 $result = $this->loadFiscalPeriods($force, 'active');
2286 if ($result < 0) {
2287 return null;
2288 }
2289
2290 $sql_list = array();
2291 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2292 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2293 $sql_list[] = "('" . $this->db->idate($fiscal_period['date_start']) . "' <= {$alias}doc_date AND {$alias}doc_date <= '" . $this->db->idate($fiscal_period['date_end']) . "')";
2294 }
2295 }
2296 self::$can_modify_bookkeeping_sql_cached[$alias] = !empty($sql_list) ? ' AND (' . implode(' OR ', $sql_list) . ')' : '';
2297 }
2298
2299 return self::$can_modify_bookkeeping_sql_cached[$alias];
2300 }
2301
2309 public function canModifyBookkeeping($id, $mode = '')
2310 {
2311 global $conf;
2312
2313 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2314 $result = $this->loadFiscalPeriods(false, 'closed');
2315
2316 if ($result < 0) {
2317 return -1;
2318 }
2319
2320 $bookkeeping = new BookKeeping($this->db);
2321 $result = $bookkeeping->fetch($id, null, $mode);
2322 if ($result <= 0) {
2323 return $result;
2324 }
2325
2326 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2327 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2328 if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2329 return 0;
2330 }
2331 }
2332 }
2333
2334 return 1;
2335 } else {
2336 $result = $this->loadFiscalPeriods(false, 'active');
2337 if ($result < 0) {
2338 return -1;
2339 }
2340
2341 $bookkeeping = new BookKeeping($this->db);
2342 $result = $bookkeeping->fetch($id, null, $mode);
2343 if ($result <= 0) {
2344 return $result;
2345 }
2346
2347 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2348 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2349 if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2350 return 1;
2351 }
2352 }
2353 }
2354
2355 return 0;
2356 }
2357 }
2358
2365 public function validBookkeepingDate($date)
2366 {
2367 global $conf;
2368
2369 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2370 $result = $this->loadFiscalPeriods(false, 'closed');
2371
2372 if ($result < 0) {
2373 return -1;
2374 }
2375
2376 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2377 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2378 if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2379 return 0;
2380 }
2381 }
2382 }
2383
2384 return 1;
2385 } else {
2386 $result = $this->loadFiscalPeriods(false, 'active');
2387 if ($result < 0) {
2388 return -1;
2389 }
2390
2391 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2392 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2393 if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2394 return 1;
2395 }
2396 }
2397 }
2398
2399 return 0;
2400 }
2401 }
2402
2410 public function loadFiscalPeriods($force = false, $mode = 'active')
2411 {
2412 global $conf;
2413
2414 if ($mode == 'active') {
2415 if (!isset($conf->cache['active_fiscal_period_cached']) || $force) {
2416 $sql = "SELECT date_start, date_end";
2417 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2418 $sql .= " WHERE entity = " . ((int) $conf->entity);
2419 $sql .= " AND statut = 0";
2420
2421 $resql = $this->db->query($sql);
2422 if (!$resql) {
2423 $this->errors[] = $this->db->lasterror();
2424 return -1;
2425 }
2426
2427 $list = array();
2428 while ($obj = $this->db->fetch_object($resql)) {
2429 $list[] = array(
2430 'date_start' => $this->db->jdate($obj->date_start),
2431 'date_end' => $this->db->jdate($obj->date_end),
2432 );
2433 }
2434 $conf->cache['active_fiscal_period_cached'] = $list;
2435 }
2436 }
2437 if ($mode == 'closed') {
2438 if (!isset($conf->cache['closed_fiscal_period_cached']) || $force) {
2439 $sql = "SELECT date_start, date_end";
2440 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2441 $sql .= " WHERE entity = " . ((int) $conf->entity);
2442 $sql .= " AND statut = 1";
2443
2444 $resql = $this->db->query($sql);
2445 if (!$resql) {
2446 $this->errors[] = $this->db->lasterror();
2447 return -1;
2448 }
2449
2450 $list = array();
2451 while ($obj = $this->db->fetch_object($resql)) {
2452 $list[] = array(
2453 'date_start' => $this->db->jdate($obj->date_start),
2454 'date_end' => $this->db->jdate($obj->date_end),
2455 );
2456 }
2457 $conf->cache['closed_fiscal_period_cached'] = $list;
2458 }
2459 }
2460
2461 return 1;
2462 }
2463
2470 public function getFiscalPeriods($filter = '')
2471 {
2472 global $conf;
2473 $list = array();
2474
2475 $sql = "SELECT rowid, label, date_start, date_end, statut";
2476 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2477 $sql .= " WHERE entity = " . ((int) $conf->entity);
2478 if (!empty($filter)) {
2479 $sql .= " AND (" . $filter . ')';
2480 }
2481 $sql .= $this->db->order('date_start', 'ASC');
2482
2483 $resql = $this->db->query($sql);
2484 if (!$resql) {
2485 $this->errors[] = $this->db->lasterror();
2486 return -1;
2487 }
2488
2489 while ($obj = $this->db->fetch_object($resql)) {
2490 $list[$obj->rowid] = array(
2491 'id' => $obj->rowid,
2492 'label' => $obj->label,
2493 'date_start' => $this->db->jdate($obj->date_start),
2494 'date_end' => $this->db->jdate($obj->date_end),
2495 'status' => $obj->statut,
2496 );
2497 }
2498
2499 return $list;
2500 }
2501
2509 public function getCountByMonthForFiscalPeriod($date_start, $date_end)
2510 {
2511 $total = 0;
2512 $list = array();
2513
2514 $sql = "SELECT YEAR(b.doc_date) as year";
2515 for ($i = 1; $i <= 12; $i++) {
2516 $sql .= ", SUM(" . $this->db->ifsql("MONTH(b.doc_date)=" . $i, "1", "0") . ") AS month" . $i;
2517 }
2518 $sql .= ", COUNT(b.rowid) as total";
2519 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as b";
2520 $sql .= " WHERE b.doc_date >= '" . $this->db->idate($date_start) . "'";
2521 $sql .= " AND b.doc_date <= '" . $this->db->idate($date_end) . "'";
2522 $sql .= " AND b.entity IN (" . getEntity('bookkeeping', 0) . ")"; // We don't share object for accountancy
2523
2524 // Get count for each month into the fiscal period
2525 if (getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")) {
2526 // TODO Analyse is done by finding record not into a closed period
2527 // Loop on each closed period
2528 $sql .= " AND b.doc_date BETWEEN 0 AND 0";
2529 } else {
2530 // Analyse closed record using the unitary flag/date on each record
2531 $sql .= " AND date_validated IS NULL";
2532 }
2533
2534 $sql .= " GROUP BY YEAR(b.doc_date)";
2535 $sql .= $this->db->order("year", 'ASC');
2536
2537 dol_syslog(__METHOD__, LOG_DEBUG);
2538 $resql = $this->db->query($sql);
2539 if (!$resql) {
2540 $this->errors[] = $this->db->lasterror();
2541 return -1;
2542 }
2543
2544 while ($obj = $this->db->fetch_object($resql)) {
2545 $total += (int) $obj->total;
2546 $year_list = array(
2547 'year' => (int) $obj->year,
2548 'count' => array(),
2549 'total' => (int) $obj->total,
2550 );
2551 for ($i = 1; $i <= 12; $i++) {
2552 $year_list['count'][$i] = (int) $obj->{'month' . $i};
2553 }
2554
2555 $list[] = $year_list;
2556 }
2557
2558 $this->db->free($resql);
2559
2560 return array(
2561 'total' => $total,
2562 'list' => $list,
2563 );
2564 }
2565
2573 public function validateMovementForFiscalPeriod($date_start, $date_end)
2574 {
2575 global $conf;
2576
2577 $now = dol_now();
2578
2579 // Specify as export : update field date_validated on selected month/year
2580 $sql = " UPDATE " . MAIN_DB_PREFIX . "accounting_bookkeeping";
2581 $sql .= " SET date_validated = '" . $this->db->idate($now) . "'";
2582 $sql .= " WHERE entity = " . ((int) $conf->entity);
2583 $sql .= " AND DATE(doc_date) >= '" . $this->db->idate($date_start) . "'";
2584 $sql .= " AND DATE(doc_date) <= '" . $this->db->idate($date_end) . "'";
2585 $sql .= " AND date_validated IS NULL";
2586
2587 dol_syslog(__METHOD__, LOG_DEBUG);
2588 $resql = $this->db->query($sql);
2589 if (!$resql) {
2590 $this->errors[] = $this->db->lasterror();
2591 return -1;
2592 }
2593
2594 return 1;
2595 }
2596
2606 public function closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account = false, $generate_bookkeeping_records = true)
2607 {
2608 global $conf, $langs, $user;
2609
2610 // Current fiscal period
2611 $fiscal_period_id = max(0, $fiscal_period_id);
2612 if (empty($fiscal_period_id)) {
2613 $langs->load('errors');
2614 $this->errors[] = $langs->trans('ErrorBadParameters');
2615 return -1;
2616 }
2617 $fiscal_period = new Fiscalyear($this->db);
2618 $result = $fiscal_period->fetch($fiscal_period_id);
2619 if ($result < 0) {
2620 $this->error = $fiscal_period->error;
2621 $this->errors = $fiscal_period->errors;
2622 return -1;
2623 } elseif (empty($fiscal_period->id)) {
2624 $langs->loadLangs(array('errors', 'compta'));
2625 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2626 return -1;
2627 }
2628
2629 // New fiscal period
2630 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2631 if (empty($new_fiscal_period_id)) {
2632 $langs->load('errors');
2633 $this->errors[] = $langs->trans('ErrorBadParameters');
2634 return -1;
2635 }
2636 $new_fiscal_period = new Fiscalyear($this->db);
2637 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2638 if ($result < 0) {
2639 $this->error = $new_fiscal_period->error;
2640 $this->errors = $new_fiscal_period->errors;
2641 return -1;
2642 } elseif (empty($new_fiscal_period->id)) {
2643 $langs->loadLangs(array('errors', 'compta'));
2644 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
2645 return -1;
2646 }
2647
2648 $error = 0;
2649 $this->db->begin();
2650
2651 $fiscal_period->statut = Fiscalyear::STATUS_CLOSED;
2652 $fiscal_period->status = Fiscalyear::STATUS_CLOSED; // Actually not used
2653 $result = $fiscal_period->update($user);
2654 if ($result < 0) {
2655 $this->error = $fiscal_period->error;
2656 $this->errors = $fiscal_period->errors;
2657 $error++;
2658 }
2659
2660 if (!$error && !empty($generate_bookkeeping_records)) {
2661 $journal_id = max(0, getDolGlobalString('ACCOUNTING_CLOSURE_DEFAULT_JOURNAL'));
2662 if (empty($journal_id)) {
2663 $langs->loadLangs(array('errors', 'accountancy'));
2664 $this->errors[] = $langs->trans('ErrorBadParameters') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2665 $error++;
2666 }
2667
2668 // Fetch journal
2669 if (!$error) {
2670 $journal = new AccountingJournal($this->db);
2671 $result = $journal->fetch($journal_id);
2672 if ($result < 0) {
2673 $this->error = $journal->error;
2674 $this->errors = $journal->errors;
2675 $error++;
2676 } elseif ($result == 0) {
2677 $langs->loadLangs(array('errors', 'accountancy'));
2678 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2679 $error++;
2680 }
2681 }
2682
2683 if (!$error) {
2684 $accounting_groups_used_for_balance_sheet_account = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_BALANCE_SHEET_ACCOUNT'))), 'strlen');
2685 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2686
2687 $pcg_type_filter = array();
2688 $tmp = array_merge($accounting_groups_used_for_balance_sheet_account, $accounting_groups_used_for_income_statement);
2689 foreach ($tmp as $item) {
2690 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2691 }
2692
2693 $sql = 'SELECT';
2694 $sql .= " t.numero_compte,";
2695 $sql .= " t.label_compte,";
2696 if ($separate_auxiliary_account) {
2697 $sql .= " t.subledger_account,";
2698 $sql .= " t.subledger_label,";
2699 }
2700 $sql .= " aa.pcg_type,";
2701 $sql .= " (SUM(t.credit) - SUM(t.debit)) as opening_balance";
2702 $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
2703 $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2704 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2705 $sql .= " AND aa.entity = ". ((int) $conf->entity);
2706 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM '.MAIN_DB_PREFIX.'accounting_system WHERE rowid = '.((int) getDolGlobalInt('CHARTOFACCOUNTS')).')';
2707 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2708 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
2709 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
2710 $sql .= ' GROUP BY t.numero_compte, t.label_compte, aa.pcg_type';
2711 if ($separate_auxiliary_account) {
2712 $sql .= ' ,t.subledger_account, t.subledger_label';
2713 }
2714 $sql .= $this->db->order("t.numero_compte", "ASC");
2715
2716 $resql = $this->db->query($sql);
2717 if (!$resql) {
2718 $this->errors[] = 'Error ' . $this->db->lasterror();
2719 dol_syslog(__METHOD__ . ' ' . join(',', $this->errors), LOG_ERR);
2720
2721 $error++;
2722 } else {
2723 $now = dol_now();
2724 $income_statement_amount = 0;
2725 while ($obj = $this->db->fetch_object($resql)) {
2726 if (in_array($obj->pcg_type, $accounting_groups_used_for_income_statement)) {
2727 $income_statement_amount += $obj->opening_balance;
2728 } else {
2729 // Insert bookkeeping record for balance sheet account
2730 $mt = $obj->opening_balance;
2731
2732 $bookkeeping = new BookKeeping($this->db);
2733 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2734 $bookkeeping->date_lim_reglement = '';
2735 $bookkeeping->doc_ref = $new_fiscal_period->label;
2736 $bookkeeping->date_creation = $now;
2737 $bookkeeping->doc_type = 'closure';
2738 $bookkeeping->fk_doc = $new_fiscal_period->id;
2739 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2740 $bookkeeping->thirdparty_code = '';
2741
2742 if ($separate_auxiliary_account) {
2743 $bookkeeping->subledger_account = $obj->subledger_account;
2744 $bookkeeping->subledger_label = $obj->subledger_label;
2745 } else {
2746 $bookkeeping->subledger_account = '';
2747 $bookkeeping->subledger_label = '';
2748 }
2749
2750 $bookkeeping->numero_compte = $obj->numero_compte;
2751 $bookkeeping->label_compte = $obj->label_compte;
2752
2753 $bookkeeping->label_operation = $new_fiscal_period->label;
2754 $bookkeeping->montant = $mt;
2755 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2756 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2757 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2758 $bookkeeping->code_journal = $journal->code;
2759 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2760 $bookkeeping->fk_user_author = $user->id;
2761 $bookkeeping->entity = $conf->entity;
2762
2763 $result = $bookkeeping->create($user);
2764 if ($result < 0) {
2765 $this->error = $bookkeeping->error;
2766 $this->errors = $bookkeeping->errors;
2767 $error++;
2768 break;
2769 }
2770 }
2771 }
2772
2773 // Insert bookkeeping record for income statement
2774 if (!$error && $income_statement_amount != 0) {
2775 $mt = $income_statement_amount;
2776 $accountingaccount = new AccountingAccount($this->db);
2777 $accountingaccount->fetch(null, getDolGlobalString($income_statement_amount < 0 ? 'ACCOUNTING_RESULT_LOSS' : 'ACCOUNTING_RESULT_PROFIT'), true);
2778
2779 $bookkeeping = new BookKeeping($this->db);
2780 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2781 $bookkeeping->date_lim_reglement = '';
2782 $bookkeeping->doc_ref = $new_fiscal_period->label;
2783 $bookkeeping->date_creation = $now;
2784 $bookkeeping->doc_type = 'closure';
2785 $bookkeeping->fk_doc = $new_fiscal_period->id;
2786 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2787 $bookkeeping->thirdparty_code = '';
2788
2789 if ($separate_auxiliary_account) {
2790 $bookkeeping->subledger_label = '';
2791 $bookkeeping->subledger_account = $obj->subledger_account;
2792 $bookkeeping->subledger_label = $obj->subledger_label;
2793 } else {
2794 $bookkeeping->subledger_account = '';
2795 $bookkeeping->subledger_label = '';
2796 }
2797
2798 $bookkeeping->numero_compte = $accountingaccount->account_number;
2799 $bookkeeping->label_compte = $accountingaccount->label;
2800
2801 $bookkeeping->label_operation = $new_fiscal_period->label;
2802 $bookkeeping->montant = $mt;
2803 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2804 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2805 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2806 $bookkeeping->code_journal = $journal->code;
2807 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2808 $bookkeeping->fk_user_author = $user->id;
2809 $bookkeeping->entity = $conf->entity;
2810
2811 $result = $bookkeeping->create($user);
2812 if ($result < 0) {
2813 $this->error = $bookkeeping->error;
2814 $this->errors = $bookkeeping->errors;
2815 $error++;
2816 }
2817 }
2818 $this->db->free($resql);
2819 }
2820 }
2821 }
2822
2823 if ($error) {
2824 $this->db->rollback();
2825 return -1;
2826 } else {
2827 $this->db->commit();
2828 return 1;
2829 }
2830 }
2831
2842 public function insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
2843 {
2844 global $conf, $langs, $user;
2845
2846 // Current fiscal period
2847 $fiscal_period_id = max(0, $fiscal_period_id);
2848 if (empty($fiscal_period_id)) {
2849 $langs->load('errors');
2850 $this->errors[] = $langs->trans('ErrorBadParameters');
2851 return -1;
2852 }
2853 $fiscal_period = new Fiscalyear($this->db);
2854 $result = $fiscal_period->fetch($fiscal_period_id);
2855 if ($result < 0) {
2856 $this->error = $fiscal_period->error;
2857 $this->errors = $fiscal_period->errors;
2858 return -1;
2859 } elseif (empty($fiscal_period->id)) {
2860 $langs->loadLangs(array('errors', 'compta'));
2861 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2862 return -1;
2863 }
2864
2865 // New fiscal period
2866 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2867 if (empty($new_fiscal_period_id)) {
2868 $langs->load('errors');
2869 $this->errors[] = $langs->trans('ErrorBadParameters');
2870 return -1;
2871 }
2872 $new_fiscal_period = new Fiscalyear($this->db);
2873 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2874 if ($result < 0) {
2875 $this->error = $new_fiscal_period->error;
2876 $this->errors = $new_fiscal_period->errors;
2877 return -1;
2878 } elseif (empty($new_fiscal_period->id)) {
2879 $langs->loadLangs(array('errors', 'compta'));
2880 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
2881 return -1;
2882 }
2883
2884 // Inventory journal
2885 $inventory_journal_id = max(0, $inventory_journal_id);
2886 if (empty($inventory_journal_id)) {
2887 $langs->load('errors');
2888 $this->errors[] = $langs->trans('ErrorBadParameters');
2889 return -1;
2890 }
2891 // Fetch journal
2892 $inventory_journal = new AccountingJournal($this->db);
2893 $result = $inventory_journal->fetch($inventory_journal_id);
2894 if ($result < 0) {
2895 $this->error = $inventory_journal->error;
2896 $this->errors = $inventory_journal->errors;
2897 return -1;
2898 } elseif ($result == 0) {
2899 $langs->loadLangs(array('errors', 'accountancy'));
2900 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('InventoryJournal');
2901 return -1;
2902 }
2903
2904 $error = 0;
2905 $this->db->begin();
2906
2907 $sql = 'SELECT t.rowid';
2908 $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
2909 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2910 $sql .= " AND code_journal = '" . $this->db->escape($inventory_journal->code) . "'";
2911 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
2912 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
2913 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
2914 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
2915
2916 $resql = $this->db->query($sql);
2917 if (!$resql) {
2918 $this->errors[] = 'Error ' . $this->db->lasterror();
2919 dol_syslog(__METHOD__ . ' ' . join(',', $this->errors), LOG_ERR);
2920
2921 $error++;
2922 } else {
2923 $now = dol_now();
2924 while ($obj = $this->db->fetch_object($resql)) {
2925 $bookkeeping = new BookKeeping($this->db);
2926 $result = $bookkeeping->fetch($obj->rowid);
2927 if ($result < 0) {
2928 $this->error = $inventory_journal->error;
2929 $this->errors = $inventory_journal->errors;
2930 $error++;
2931 break;
2932 } elseif ($result == 0) {
2933 $langs->loadLangs(array('errors', 'accountancy'));
2934 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('LineId') . ': ' . $obj->rowid;
2935 $error++;
2936 break;
2937 }
2938
2939 $bookkeeping->id = 0;
2940 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2941 $bookkeeping->doc_ref = $new_fiscal_period->label;
2942 $bookkeeping->date_creation = $now;
2943 $bookkeeping->doc_type = 'accounting_reversal';
2944 $bookkeeping->fk_doc = $new_fiscal_period->id;
2945 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2946
2947 $bookkeeping->montant = -$bookkeeping->montant;
2948 $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
2949 $old_debit = $bookkeeping->debit;
2950 $bookkeeping->debit = $bookkeeping->credit;
2951 $bookkeeping->credit = $old_debit;
2952
2953 $bookkeeping->fk_user_author = $user->id;
2954 $bookkeeping->entity = $conf->entity;
2955
2956 $result = $bookkeeping->create($user);
2957 if ($result < 0) {
2958 $this->error = $bookkeeping->error;
2959 $this->errors = $bookkeeping->errors;
2960 $error++;
2961 break;
2962 }
2963 }
2964 $this->db->free($resql);
2965 }
2966
2967 if ($error) {
2968 $this->db->rollback();
2969 return -1;
2970 } else {
2971 $this->db->commit();
2972 return 1;
2973 }
2974 }
2975}
2976
2981{
2985 public $id;
2986
2987 public $doc_date = '';
2988 public $doc_type;
2989 public $doc_ref;
2990
2994 public $fk_doc;
2995
2999 public $fk_docdet;
3000
3001 public $thirdparty_code;
3002 public $subledger_account;
3003 public $subledger_label;
3004 public $numero_compte;
3005 public $label_compte;
3006 public $label_operation;
3007 public $debit;
3008 public $credit;
3009
3014 public $montant;
3015
3019 public $amount;
3020
3024 public $multicurrency_amount;
3025
3029 public $multicurrency_code;
3030
3034 public $sens;
3035 public $lettering_code;
3036 public $date_lettering;
3037
3041 public $fk_user_author;
3042
3043 public $import_key;
3044 public $code_journal;
3045 public $journal_label;
3046 public $piece_num;
3047
3051 public $date_creation;
3052
3056 public $date_modification;
3057
3061 public $date_export;
3062
3066 public $date_validation;
3067
3071 public $date_lim_reglement;
3072}
length_accountg($account)
Return General accounting account with defined length (used for product and miscellaneous)
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:447
Class to manage accounting accounts.
Class to manage accounting journals.
Class to manage Ledger (General Ledger and Subledger)
fetchAllBalance($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND', $option=0)
Load object in memory from the database.
closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account=false, $generate_bookkeeping_records=true)
Close fiscal period.
getCountByMonthForFiscalPeriod($date_start, $date_end)
Get list of count by month into the fiscal period.
create(User $user, $notrigger=false)
Create object into database.
createStd(User $user, $notrigger=false, $mode='')
Create object into database.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
select_account($selectid, $htmlname='account', $showempty=0, $event=array(), $select_in=0, $select_out=0, $aabase='')
Return list of accounts with label by chart of accounts.
update(User $user, $notrigger=false, $mode='')
Update object into database.
getNextNumMvt($mode='')
Return next number movement.
get_compte_desc($account=null)
Description of accounting account.
__construct(DoliDB $db)
Constructor.
createFromClone(User $user, $fromid)
Load an object from its id and create a new one in database.
deleteByImportkey($importkey, $mode='')
Delete bookkeeping by importkey.
getRootAccount($account=null)
Return id and description of a root accounting account.
transformTransaction($direction=0, $piece_num='')
Transform transaction.
insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
Insert accounting reversal into the inventory journal of the new fiscal period.
fetchPerMvt($piecenum, $mode='')
Load an accounting document into memory from database.
updateByMvt($piece_num='', $field='', $value='', $mode='')
Update accounting movement.
deleteMvtNum($piecenum, $mode='')
Delete bookkeeping by piece number.
getFiscalPeriods($filter='')
Get list of fiscal period.
validBookkeepingDate($date)
Is the bookkeeping date valid (on an open period or not on a closed period) ?
deleteByYearAndJournal($delyear=0, $journal='', $mode='', $delmonth=0)
Delete bookkeeping by year.
loadFiscalPeriods($force=false, $mode='active')
Load list of active fiscal period.
fetchAllByAccount($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND', $option=0, $countonly=0)
Load object in memory from the database in ->lines.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
getCanModifyBookkeepingSQL($alias='', $force=false)
Get SQL string for check if the bookkeeping can be modified or deleted ? (cached)
fetch($id, $ref=null, $mode='')
Load object in memory from the database.
canModifyBookkeeping($id, $mode='')
Is the bookkeeping can be modified or deleted ?
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND', $showAlreadyExportMovements=1)
Load object in memory from the database.
fetchAllPerMvt($piecenum, $mode='')
Load all informations of accountancy document.
validateMovementForFiscalPeriod($date_start, $date_end)
Validate all movement between the specified dates.
export_bookkeeping($model='ebp')
Export bookkeeping.
Class BookKeepingLine.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class to manage Dolibarr database access.
Class to manage fiscal year.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Class to manage Dolibarr users.
dolSqlDateFilter($datefield, $day_date, $month_date, $year_date, $excludefirstand=0, $gm=false)
Generate a SQL string to make a filter into a range (for second of date until last second of date).
Definition date.lib.php:376
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)
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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.