dolibarr 22.0.5
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-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
4 * Copyright (C) 2015-2020 Florian Henry <florian.henry@open-concept.pro>
5 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2024 Jose MARTINEZ <jose.martinez@pichinov.com>
8 * Copyright (C) 2025 Nicolas Barrouillet <nicolas@pragma-tech.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
30// Class
31require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
32require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/class/fiscalyear.class.php';
34require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
35require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
36
41{
45 public $element = 'accountingbookkeeping';
46
50 public $table_element = 'accounting_bookkeeping';
51
55 public $entity;
56
60 public $lines = array();
61
65 public $id;
66
70 public $doc_date;
71
75 public $date_lim_reglement;
76
80 public $doc_type;
81
85 public $doc_ref;
86
90 public $fk_doc;
91
95 public $fk_docdet;
96
100 public $thirdparty_code;
101
105 public $subledger_account;
106
110 public $subledger_label;
111
115 public $numero_compte;
116
120 public $label_compte;
121
125 public $label_operation;
126
130 public $debit;
131
135 public $credit;
136
141 public $montant;
142
147 public $amount;
148
152 public $sens;
153
157 public $fk_user_author;
158
162 public $import_key;
163
167 public $code_journal;
168
172 public $journal_label;
173
177 public $piece_num;
178
182 public $ref;
183
187 public $linesmvt = array();
188
192 public $linesexport = array();
193
197 public $date_export;
198
202 public $picto = 'generic';
203
207 public static $can_modify_bookkeeping_sql_cached;
208
212 public $warnings = array();
213
214
220 public function __construct(DoliDB $db)
221 {
222 $this->db = $db;
223 }
224
232 public function create(User $user, $notrigger = 0)
233 {
234 global $conf, $langs;
235
236 dol_syslog(__METHOD__, LOG_DEBUG);
237
238 $error = 0;
239
240 // Clean parameters</center>
241 if (isset($this->doc_type)) {
242 $this->doc_type = trim($this->doc_type);
243 }
244 if (isset($this->doc_ref)) {
245 $this->doc_ref = trim($this->doc_ref);
246 $this->doc_ref = dol_trunc($this->doc_ref, 300); // We limit to 300 chars to avoid problems with too long ref in DB
247 }
248 if (isset($this->fk_doc)) {
249 $this->fk_doc = (int) $this->fk_doc;
250 }
251 if (isset($this->fk_docdet)) {
252 $this->fk_docdet = (int) $this->fk_docdet;
253 }
254 if (isset($this->thirdparty_code)) {
255 $this->thirdparty_code = trim($this->thirdparty_code);
256 }
257 if (isset($this->subledger_account)) {
258 $this->subledger_account = trim($this->subledger_account);
259 }
260 if (isset($this->subledger_label)) {
261 $this->subledger_label = trim($this->subledger_label);
262 }
263 if (isset($this->numero_compte)) {
264 $this->numero_compte = trim($this->numero_compte);
265 }
266 if (isset($this->label_compte)) {
267 $this->label_compte = trim($this->label_compte);
268 }
269 if (isset($this->label_operation)) {
270 $this->label_operation = trim($this->label_operation);
271 }
272 if (isset($this->debit)) {
273 $this->debit = (float) $this->debit;
274 }
275 if (isset($this->credit)) {
276 $this->credit = (float) $this->credit;
277 }
278 if (isset($this->montant)) {
279 $this->montant = (float) $this->montant;
280 }
281 if (isset($this->amount)) {
282 $this->amount = (float) $this->amount;
283 }
284 if (isset($this->sens)) {
285 $this->sens = trim($this->sens);
286 }
287 if (isset($this->import_key)) {
288 $this->import_key = trim($this->import_key);
289 }
290 if (isset($this->code_journal)) {
291 $this->code_journal = trim($this->code_journal);
292 }
293 if (isset($this->journal_label)) {
294 $this->journal_label = trim($this->journal_label);
295 }
296 if (isset($this->piece_num)) {
297 $this->piece_num = (int) $this->piece_num;
298 }
299 if (empty($this->debit)) {
300 $this->debit = 0.0;
301 }
302 if (empty($this->credit)) {
303 $this->credit = 0.0;
304 }
305
306 $result = $this->validBookkeepingDate($this->doc_date); // Check date according to ACCOUNTANCY_FISCAL_PERIOD_MODE.
307 if ($result < 0) {
308 return -1;
309 } elseif ($result == 0) {
310 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
311 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
312 } else {
313 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
314 }
315 return -1;
316 }
317
318 // Check parameters
319 if (($this->numero_compte == "") || $this->numero_compte == '-1' || $this->numero_compte == 'NotDefined') {
320 $langs->loadLangs(array("errors"));
321 if (in_array($this->doc_type, array('bank', 'expense_report'))) {
322 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet, $this->doc_type);
323 } else {
324 //$this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->doc_ref, $this->label_compte);
325 $mesg = $this->doc_ref.', '.$langs->trans("AccountAccounting").': '.($this->numero_compte != -1 ? $this->numero_compte : $langs->trans("Unknown"));
326 if ($this->subledger_account && $this->subledger_account != $this->numero_compte) {
327 $mesg .= ', '.$langs->trans("SubledgerAccount").': '.$this->subledger_account;
328 }
329 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForLine', $mesg);
330 }
331
332 return -1;
333 }
334
335 $this->db->begin();
336
337 $this->piece_num = 0;
338 $this->ref = '';
339
340 // First check if line not yet already in bookkeeping.
341 // Note that we must include 'doc_type - fk_doc - numero_compte - label - subledger_account (if not empty)' to be sure to have unicity of line (because we may have several lines
342 // with same doc_type, fk_doc, numero_compte for 1 invoice line when using localtaxes with same account)
343 // WARNING: This is not reliable, label may have been modified. This is just a small protection.
344 // The page that make transfer make the test on couple (doc_type - fk_doc) only.
345 $sql = "SELECT count(*) as nb";
346 $sql .= " FROM ".$this->db->prefix().$this->table_element;
347 $sql .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'";
348 $sql .= " AND fk_doc = ".((int) $this->fk_doc);
349 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
350 // DO NOT USE THIS IN PRODUCTION. This will generate a lot of trouble into reports and will corrupt database (by generating duplicate entries.
351 $sql .= " AND fk_docdet = ".((int) $this->fk_docdet); // This field can be 0 if record is for several lines
352 }
353 $sql .= " AND numero_compte = '".$this->db->escape($this->numero_compte)."'";
354 $sql .= " AND label_operation = '".$this->db->escape($this->label_operation)."'";
355 if (!empty($this->subledger_account)) {
356 $sql .= " AND subledger_account = '".$this->db->escape($this->subledger_account)."'";
357 }
358 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
359
360 $resql = $this->db->query($sql);
361
362 if ($resql) {
363 $row = $this->db->fetch_object($resql);
364 if ($row->nb == 0) { // Not already into bookkeeping
365 // Check to know if piece_num already exists for data we try to insert to reuse the same value
366 $sqlnum = "SELECT piece_num, ref";
367 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
368 $sqlnum .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'"; // For example doc_type = 'bank'
369 $sqlnum .= " AND fk_doc = ".((int) $this->fk_doc);
370 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
371 // fk_docdet is rowid into llx_bank or llx_facturedet or llx_facturefourndet, or ...
372 $sqlnum .= " AND fk_docdet = ".((int) $this->fk_docdet);
373 }
374 $sqlnum .= " AND doc_ref = '".$this->db->escape($this->doc_ref)."'"; // ref of source object
375 $sqlnum .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
376
377 dol_syslog(get_class($this).":: create sqlnum=".$sqlnum, LOG_DEBUG);
378 $resqlnum = $this->db->query($sqlnum);
379 if ($resqlnum) {
380 $num = $this->db->num_rows($resqlnum);
381 if ($num > 0) {
382 $objnum = $this->db->fetch_object($resqlnum);
383 $this->piece_num = $objnum->piece_num;
384 $this->ref = $objnum->ref;
385 } else {
386 $this->piece_num = 0;
387 $this->ref = '';
388 }
389 }
390
391 dol_syslog(get_class($this)."::create this->piece_num=".$this->piece_num, LOG_DEBUG);
392 if (empty($this->piece_num)) {
393 $sqlnum = "SELECT MAX(piece_num)+1 as maxpiecenum";
394 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
395 $sqlnum .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
396
397 $resqlnum = $this->db->query($sqlnum);
398 if ($resqlnum) {
399 $objnum = $this->db->fetch_object($resqlnum);
400 $this->piece_num = $objnum->maxpiecenum;
401 }
402
403 $this->ref = $this->getNextNumRef();
404 dol_syslog(get_class($this).":: create now this->piece_num={$this->piece_num}, this->ref={$this->ref}", LOG_DEBUG);
405 }
406 if (empty($this->piece_num)) {
407 $this->piece_num = 1;
408 }
409
410 $now = dol_now();
411
412 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element." (";
413 $sql .= "doc_date";
414 $sql .= ", date_lim_reglement";
415 $sql .= ", doc_type";
416 $sql .= ", doc_ref";
417 $sql .= ", fk_doc";
418 $sql .= ", fk_docdet";
419 $sql .= ", thirdparty_code";
420 $sql .= ", subledger_account";
421 $sql .= ", subledger_label";
422 $sql .= ", numero_compte";
423 $sql .= ", label_compte";
424 $sql .= ", label_operation";
425 $sql .= ", debit";
426 $sql .= ", credit";
427 $sql .= ", montant";
428 $sql .= ", sens";
429 $sql .= ", fk_user_author";
430 $sql .= ", date_creation";
431 $sql .= ", code_journal";
432 $sql .= ", journal_label";
433 $sql .= ", piece_num";
434 $sql .= ", ref";
435 $sql .= ', entity';
436 $sql .= ") VALUES (";
437 $sql .= "'".$this->db->idate($this->doc_date)."'";
438 $sql .= ", ".(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL');
439 $sql .= ", '".$this->db->escape($this->doc_type)."'";
440 $sql .= ", '".$this->db->escape($this->doc_ref)."'";
441 $sql .= ", ".((int) $this->fk_doc);
442 $sql .= ", ".((int) $this->fk_docdet);
443 $sql .= ", ".(!empty($this->thirdparty_code) ? ("'".$this->db->escape($this->thirdparty_code)."'") : "NULL");
444 $sql .= ", ".(!empty($this->subledger_account) ? ("'".$this->db->escape($this->subledger_account)."'") : "NULL");
445 $sql .= ", ".(!empty($this->subledger_label) ? ("'".$this->db->escape($this->subledger_label)."'") : "NULL");
446 $sql .= ", '".$this->db->escape($this->numero_compte)."'";
447 $sql .= ", ".(!empty($this->label_compte) ? ("'".$this->db->escape($this->label_compte)."'") : "NULL");
448 $sql .= ", '".$this->db->escape($this->label_operation)."'";
449 $sql .= ", ".((float) $this->debit);
450 $sql .= ", ".((float) $this->credit);
451 $sql .= ", ".((float) $this->montant);
452 $sql .= ", ".(!empty($this->sens) ? ("'".$this->db->escape($this->sens)."'") : "NULL");
453 $sql .= ", '".$this->db->escape((string) $this->fk_user_author)."'";
454 $sql .= ", '".$this->db->idate($now)."'";
455 $sql .= ", '".$this->db->escape($this->code_journal)."'";
456 $sql .= ", ".(!empty($this->journal_label) ? ("'".$this->db->escape($this->journal_label)."'") : "NULL");
457 $sql .= ", ".((int) $this->piece_num);
458 $sql .= ", '".$this->db->escape($this->ref)."'";
459 $sql .= ", ".(!isset($this->entity) ? $conf->entity : $this->entity);
460 $sql .= ")";
461
462 $resql = $this->db->query($sql);
463 if ($resql) {
464 $id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
465
466 if ($id > 0) {
467 $this->id = $id;
468 $result = 0;
469 } else {
470 $result = -2;
471 $error++;
472 $this->errors[] = 'Error Create Error '.$result.' lecture ID';
473 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
474 }
475 } else {
476 $result = -1;
477 $error++;
478 $this->errors[] = 'Error '.$this->db->lasterror();
479 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
480 }
481 } else { // Already exists
482 $result = -3;
483 $error++;
484 $this->error = 'BookkeepingRecordAlreadyExists';
485 dol_syslog(__METHOD__.' '.$this->error, LOG_WARNING);
486 }
487 } else {
488 $result = -5;
489 $error++;
490 $this->errors[] = 'Error '.$this->db->lasterror();
491 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
492 }
493
494 // Call triggers
495 if (! $error && ! $notrigger) {
496 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
497 if ($result < 0) {
498 $error++;
499 }
500 }
501
502 // Commit or rollback
503 if ($error) {
504 $this->db->rollback();
505 return -1 * $error;
506 } else {
507 $this->db->commit();
508 return $result;
509 }
510 }
511
529 public function createFromValues($doc_date, $doc_ref, $doc_type, $fk_doc, $fk_docdet, $numero_compte, $label_compte, $label_operation, $amount, $code_journal, $journal_label, $subledger_account)
530 {
531 global $conf, $langs, $user;
532
533 $result = 0;
534
535 if (!empty($amount)) {
536 $this->doc_date = $doc_date;
537 $this->doc_ref = $doc_ref;
538 $this->doc_type = $doc_type;
539 $this->fk_doc = $fk_doc;
540 $this->fk_docdet = $fk_docdet;
541
542 $this->numero_compte = $numero_compte;
543 $this->label_compte = $label_compte;
544
545 $this->label_operation = $label_operation;
546 $this->subledger_account = $subledger_account;
547
548 $this->montant = $amount;
549 $this->sens = ($amount >= 0) ? 'D' : 'C';
550 $this->debit = ($amount >= 0 ? $amount : 0);
551 $this->credit = ($amount < 0 ? -$amount : 0);
552
553 $this->code_journal = $code_journal;
554 $this->journal_label = $journal_label;
555
556 $this->fk_user_author = $user->id;
557 $this->entity = $conf->entity;
558
559 $result = $this->create($user);
560 if ($result < 0) {
561 if ($this->error == 'BookkeepingRecordAlreadyExists') {
562 $warning = $langs->trans('WarningBookkeepingRecordAlreadyExists', $this->doc_type, $this->fk_doc, $this->fk_docdet);
563 $this->warnings[] = $warning;
564 dol_syslog(__METHOD__.' '.$warning, LOG_WARNING);
565 } else {
566 dol_syslog(__METHOD__.' '.$this->errorsToString(), LOG_ERR);
567 }
568 }
569 }
570
571 return $result;
572 }
573
584 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
585 {
586 global $db, $conf, $langs;
587 global $dolibarr_main_authentication, $dolibarr_main_demo;
588 global $menumanager, $hookmanager;
589
590 if (!empty($conf->dol_no_mouse_hover)) {
591 $notooltip = 1; // Force disable tooltips
592 }
593
594 $result = '';
595 $companylink = '';
596
597 $label = '<u>'.$langs->trans("Transaction").'</u>';
598 $label .= '<br>';
599 $label .= '<b>'.$langs->trans('NumberingShort').':</b> '.$this->piece_num;
600 $label .= '<br>';
601 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
602
603 $url = DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?piece_num='.$this->piece_num;
604
605 if ($option != 'nolink') {
606 // Add param to save lastsearch_values or not
607 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
608 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
609 $add_save_lastsearch_values = 1;
610 }
611 if ($add_save_lastsearch_values) {
612 $url .= '&save_lastsearch_values=1';
613 }
614 }
615
616 $linkclose = '';
617 if (empty($notooltip)) {
618 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
619 $label = $langs->trans("ShowTransaction");
620 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
621 }
622 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
623 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
624 } else {
625 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
626 }
627
628 $linkstart = '<a href="'.$url.'"';
629 $linkstart .= $linkclose.'>';
630 $linkend = '</a>';
631
632 $result .= $linkstart;
633 if ($withpicto) {
634 $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);
635 }
636 if ($withpicto != 2) {
637 $result .= $this->piece_num;
638 }
639 $result .= $linkend;
640 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
641
642 global $action;
643 $hookmanager->initHooks(array($this->element . 'dao'));
644 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
645 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
646 if ($reshook > 0) {
647 $result = $hookmanager->resPrint;
648 } else {
649 $result .= $hookmanager->resPrint;
650 }
651 return $result;
652 }
653
662 public function createStd(User $user, $notrigger = 0, $mode = '')
663 {
664 global $conf, $langs;
665
666 $langs->loadLangs(array("accountancy", "bills", "compta"));
667
668 dol_syslog(__METHOD__, LOG_DEBUG);
669
670 $error = 0;
671
672 // Clean parameters
673 if (isset($this->doc_type)) {
674 $this->doc_type = trim($this->doc_type);
675 }
676 if (isset($this->doc_ref)) {
677 $this->doc_ref = trim($this->doc_ref);
678 }
679 if (isset($this->fk_doc)) {
680 $this->fk_doc = (int) $this->fk_doc;
681 }
682 if (isset($this->fk_docdet)) {
683 $this->fk_docdet = (int) $this->fk_docdet;
684 }
685 if (isset($this->thirdparty_code)) {
686 $this->thirdparty_code = trim($this->thirdparty_code);
687 }
688 if (isset($this->subledger_account)) {
689 $this->subledger_account = trim($this->subledger_account);
690 }
691 if (isset($this->subledger_label)) {
692 $this->subledger_label = trim($this->subledger_label);
693 }
694 if (isset($this->numero_compte)) {
695 $this->numero_compte = trim($this->numero_compte);
696 }
697 if (isset($this->label_compte)) {
698 $this->label_compte = trim($this->label_compte);
699 }
700 if (isset($this->label_operation)) {
701 $this->label_operation = trim($this->label_operation);
702 }
703 if (isset($this->sens)) {
704 $this->sens = trim($this->sens);
705 }
706 if (isset($this->import_key)) {
707 $this->import_key = trim($this->import_key);
708 }
709 if (isset($this->code_journal)) {
710 $this->code_journal = trim($this->code_journal);
711 }
712 if (isset($this->journal_label)) {
713 $this->journal_label = trim($this->journal_label);
714 }
715 if (isset($this->piece_num)) {
716 $this->piece_num = (int) $this->piece_num;
717 }
718 if (empty($this->debit)) {
719 $this->debit = 0;
720 }
721 if (empty($this->credit)) {
722 $this->credit = 0;
723 }
724 if (empty($this->montant)) {
725 $this->montant = 0;
726 }
727
728 $result = $this->validBookkeepingDate($this->doc_date);
729 if ($result < 0) {
730 return -1;
731 } elseif ($result == 0) {
732 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
733 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
734 } else {
735 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
736 }
737 return -1;
738 }
739
740 $this->debit = (float) price2num($this->debit, 'MT');
741 $this->credit = (float) price2num($this->credit, 'MT');
742 $this->montant = (float) price2num($this->montant, 'MT');
743
744 $now = dol_now();
745
746 // Check parameters
747 $this->journal_label = $langs->trans($this->journal_label);
748
749 // Insert request
750 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.$mode.' (';
751 $sql .= 'doc_date,';
752 $sql .= 'date_lim_reglement,';
753 $sql .= 'doc_type,';
754 $sql .= 'doc_ref,';
755 $sql .= 'fk_doc,';
756 $sql .= 'fk_docdet,';
757 $sql .= 'thirdparty_code,';
758 $sql .= 'subledger_account,';
759 $sql .= 'subledger_label,';
760 $sql .= 'numero_compte,';
761 $sql .= 'label_compte,';
762 $sql .= 'label_operation,';
763 $sql .= 'debit,';
764 $sql .= 'credit,';
765 $sql .= 'montant,';
766 $sql .= 'sens,';
767 $sql .= 'fk_user_author,';
768 $sql .= 'date_creation,';
769 $sql .= 'code_journal,';
770 $sql .= 'journal_label,';
771 $sql .= 'piece_num,';
772 $sql .= 'ref,';
773 $sql .= 'entity';
774 $sql .= ') VALUES (';
775 $sql .= ' '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'NULL').',';
776 $sql .= ' '.(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL').',';
777 $sql .= ' '.(!isset($this->doc_type) ? 'NULL' : "'".$this->db->escape($this->doc_type)."'").',';
778 $sql .= ' '.(!isset($this->doc_ref) ? 'NULL' : "'".$this->db->escape($this->doc_ref)."'").',';
779 $sql .= ' '.(empty($this->fk_doc) ? '0' : (int) $this->fk_doc).',';
780 $sql .= ' '.(empty($this->fk_docdet) ? '0' : (int) $this->fk_docdet).',';
781 $sql .= ' '.(!isset($this->thirdparty_code) ? 'NULL' : "'".$this->db->escape($this->thirdparty_code)."'").',';
782 $sql .= ' '.(!isset($this->subledger_account) ? 'NULL' : "'".$this->db->escape($this->subledger_account)."'").',';
783 $sql .= ' '.(!isset($this->subledger_label) ? 'NULL' : "'".$this->db->escape($this->subledger_label)."'").',';
784 $sql .= ' '.(!isset($this->numero_compte) ? 'NULL' : "'".$this->db->escape($this->numero_compte)."'").',';
785 $sql .= ' '.(!isset($this->label_compte) ? 'NULL' : "'".$this->db->escape($this->label_compte)."'").',';
786 $sql .= ' '.(!isset($this->label_operation) ? 'NULL' : "'".$this->db->escape($this->label_operation)."'").',';
787 $sql .= ' '.(!isset($this->debit) ? 'NULL' : $this->debit).',';
788 $sql .= ' '.(!isset($this->credit) ? 'NULL' : $this->credit).',';
789 $sql .= ' '.(!isset($this->montant) ? 'NULL' : $this->montant).',';
790 $sql .= ' '.(!isset($this->sens) ? 'NULL' : "'".$this->db->escape($this->sens)."'").',';
791 $sql .= ' '.((int) $user->id).',';
792 $sql .= ' '."'".$this->db->idate($now)."',";
793 $sql .= ' '.(empty($this->code_journal) ? 'NULL' : "'".$this->db->escape($this->code_journal)."'").',';
794 $sql .= ' '.(empty($this->journal_label) ? 'NULL' : "'".$this->db->escape($this->journal_label)."'").',';
795 $sql .= ' '.(empty($this->piece_num) ? 'NULL' : $this->db->escape((string) $this->piece_num)).',';
796 $sql .= ' '.(empty($this->ref) ? "''" : "'".$this->db->escape($this->ref)."'").',';
797 $sql .= ' '.(!isset($this->entity) ? $conf->entity : $this->entity);
798 $sql .= ')';
799
800 $this->db->begin();
801
802 $resql = $this->db->query($sql);
803 if (!$resql) {
804 $error++;
805 $this->errors[] = 'Error '.$this->db->lasterror();
806 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
807 }
808
809 if (!$error) {
810 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element.$mode);
811 // Call triggers
812 if (! $notrigger) {
813 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
814 if ($result < 0) {
815 $error++;
816 }
817 }
818 }
819
820 // Commit or rollback
821 if ($error) {
822 $this->db->rollback();
823
824 return -1 * $error;
825 } else {
826 $this->db->commit();
827
828 return $this->id;
829 }
830 }
831
840 public function fetch($id, $ref = null, $mode = '')
841 {
842 global $conf;
843
844 dol_syslog(__METHOD__, LOG_DEBUG);
845
846 $sql = 'SELECT';
847 $sql .= ' t.rowid,';
848 $sql .= " t.doc_date,";
849 $sql .= " t.date_lim_reglement,";
850 $sql .= " t.doc_type,";
851 $sql .= " t.doc_ref,";
852 $sql .= " t.fk_doc,";
853 $sql .= " t.fk_docdet,";
854 $sql .= " t.thirdparty_code,";
855 $sql .= " t.subledger_account,";
856 $sql .= " t.subledger_label,";
857 $sql .= " t.numero_compte,";
858 $sql .= " t.label_compte,";
859 $sql .= " t.label_operation,";
860 $sql .= " t.debit,";
861 $sql .= " t.credit,";
862 $sql .= " t.montant as amount,";
863 $sql .= " t.sens,";
864 $sql .= " t.fk_user_author,";
865 $sql .= " t.import_key,";
866 $sql .= " t.code_journal,";
867 $sql .= " t.journal_label,";
868 $sql .= " t.piece_num,";
869 $sql .= " t.ref,";
870 $sql .= " t.date_creation,";
871 // In llx_accounting_bookkeeping_tmp, date_export
872 if (!$mode) {
873 $sql .= " t.date_export,";
874 }
875 $sql .= " t.date_validated as date_validation";
876 $sql .= ' FROM '.$this->db->prefix().$this->table_element.$mode.' as t';
877 $sql .= ' WHERE 1 = 1';
878 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
879 if (null !== $ref) {
880 $sql .= " AND t.rowid = ".((int) $ref);
881 } else {
882 $sql .= " AND t.rowid = ".((int) $id);
883 }
884
885 $resql = $this->db->query($sql);
886 if ($resql) {
887 $numrows = $this->db->num_rows($resql);
888 if ($numrows) {
889 $obj = $this->db->fetch_object($resql);
890
891 $this->id = $obj->rowid;
892
893 $this->doc_date = $this->db->jdate($obj->doc_date);
894 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
895 $this->doc_type = $obj->doc_type;
896 $this->doc_ref = $obj->doc_ref;
897 $this->fk_doc = $obj->fk_doc;
898 $this->fk_docdet = $obj->fk_docdet;
899 $this->thirdparty_code = $obj->thirdparty_code;
900 $this->subledger_account = $obj->subledger_account;
901 $this->subledger_label = $obj->subledger_label;
902 $this->numero_compte = $obj->numero_compte;
903 $this->label_compte = $obj->label_compte;
904 $this->label_operation = $obj->label_operation;
905 $this->debit = $obj->debit;
906 $this->credit = $obj->credit;
907 $this->montant = $obj->amount;
908 $this->amount = $obj->amount;
909 $this->sens = $obj->sens;
910 $this->fk_user_author = $obj->fk_user_author;
911 $this->import_key = $obj->import_key;
912 $this->code_journal = $obj->code_journal;
913 $this->journal_label = $obj->journal_label;
914 $this->piece_num = $obj->piece_num;
915 $this->date_creation = $this->db->jdate($obj->date_creation);
916 if (!$mode) {
917 $this->date_export = $this->db->jdate($obj->date_export);
918 }
919 $this->ref = $obj->ref;
920 $this->date_validation = isset($obj->date_validation) ? $this->db->jdate($obj->date_validation) : '';
921 }
922 $this->db->free($resql);
923
924 if ($numrows) {
925 return 1;
926 } else {
927 return 0;
928 }
929 } else {
930 $this->errors[] = 'Error '.$this->db->lasterror();
931 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
932
933 return -1;
934 }
935 }
936
937
951 public function fetchAllByAccount($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0, $countonly = 0)
952 {
953 global $conf;
954
955 dol_syslog(__METHOD__, LOG_DEBUG);
956
957 $this->lines = array();
958 $num = 0;
959
960 $sql = 'SELECT';
961 if ($countonly) {
962 $sql .= ' COUNT(t.rowid) as nb';
963 } else {
964 $sql .= ' t.rowid,';
965 $sql .= " t.doc_date,";
966 $sql .= " t.doc_type,";
967 $sql .= " t.doc_ref,";
968 $sql .= " t.fk_doc,";
969 $sql .= " t.fk_docdet,";
970 $sql .= " t.thirdparty_code,";
971 $sql .= " t.subledger_account,";
972 $sql .= " t.subledger_label,";
973 $sql .= " t.numero_compte,";
974 $sql .= " t.label_compte,";
975 $sql .= " t.label_operation,";
976 $sql .= " t.debit,";
977 $sql .= " t.credit,";
978 $sql .= " t.montant as amount,";
979 $sql .= " t.sens,";
980 $sql .= " t.multicurrency_amount,";
981 $sql .= " t.multicurrency_code,";
982 $sql .= " t.lettering_code,";
983 $sql .= " t.date_lettering,";
984 $sql .= " t.fk_user_author,";
985 $sql .= " t.import_key,";
986 $sql .= " t.code_journal,";
987 $sql .= " t.journal_label,";
988 $sql .= " t.piece_num,";
989 $sql .= " t.ref,";
990 $sql .= " t.date_creation,";
991 $sql .= " t.date_export,";
992 $sql .= " t.date_validated as date_validation,";
993 $sql .= " t.date_lim_reglement,";
994 $sql .= " t.import_key";
995 }
996 // Manage filter
997 $sqlwhere = array();
998 if (count($filter) > 0) {
999 foreach ($filter as $key => $value) {
1000 if ($key == 't.doc_date>=') {
1001 $sqlwhere[] = "t.doc_date >= '".$this->db->idate((int) $value)."'";
1002 } elseif ($key == 't.doc_date<=') {
1003 $sqlwhere[] = "t.doc_date <= '".$this->db->idate((int) $value)."'";
1004 } elseif ($key == 't.doc_date>') {
1005 $sqlwhere[] = "t.doc_date > '".$this->db->idate((int) $value)."'";
1006 } elseif ($key == 't.doc_date<') {
1007 $sqlwhere[] = "t.doc_date < '".$this->db->idate((int) $value)."'";
1008 } elseif ($key == 't.numero_compte>=') {
1009 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1010 } elseif ($key == 't.numero_compte<=') {
1011 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1012 } elseif ($key == 't.subledger_account>=') {
1013 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1014 } elseif ($key == 't.subledger_account<=') {
1015 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1016 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1017 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
1018 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1019 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($this->db->escapeforlike($value))."%'";
1020 } elseif ($key == 't.date_creation>=') {
1021 $sqlwhere[] = "t.date_creation >= '".$this->db->idate((int) $value)."'";
1022 } elseif ($key == 't.date_creation<=') {
1023 $sqlwhere[] = "t.date_creation <= '".$this->db->idate((int) $value)."'";
1024 } elseif ($key == 't.date_export>=') {
1025 $sqlwhere[] = "t.date_export >= '".$this->db->idate((int) $value)."'";
1026 } elseif ($key == 't.date_export<=') {
1027 $sqlwhere[] = "t.date_export <= '".$this->db->idate((int) $value)."'";
1028 } elseif ($key == 't.date_validated>=') {
1029 $sqlwhere[] = "t.date_validated >= '".$this->db->idate((int) $value)."'";
1030 } elseif ($key == 't.date_validated<=') {
1031 $sqlwhere[] = "t.date_validated <= '".$this->db->idate((int) $value)."'";
1032 } elseif ($key == 't.date_lim_reglement>=') {
1033 $sqlwhere[] = "t.date_lim_reglement>='".$this->db->idate((int) $value)."'";
1034 } elseif ($key == 't.date_lim_reglement<=') {
1035 $sqlwhere[] = "t.date_lim_reglement<='".$this->db->idate((int) $value)."'";
1036 } elseif ($key == 't.credit' || $key == 't.debit') {
1037 $sqlwhere[] = natural_search($key, $value, 1, 1);
1038 } elseif ($key == 't.reconciled_option') {
1039 $sqlwhere[] = 't.lettering_code IS NULL';
1040 } elseif ($key == 't.code_journal' && !empty($value)) {
1041 if (is_array($value)) {
1042 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1043 } else {
1044 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1045 }
1046 } elseif ($key == 't.search_accounting_code_in' && !empty($value)) {
1047 $sqlwhere[] = "t.numero_compte IN (".$this->db->sanitize($value, 1).")";
1048 } else {
1049 $sqlwhere[] = natural_search($key, $value, 0, 1);
1050 }
1051 }
1052 }
1053 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1054 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1055 if (count($sqlwhere) > 0) {
1056 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1057 }
1058 // Filter by ledger account or subledger account
1059 if (!empty($option)) {
1060 $sql .= " AND t.subledger_account IS NOT NULL";
1061 $sql .= " AND t.subledger_account <> ''";
1062 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
1063 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
1064 } else {
1065 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
1066 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
1067 }
1068
1069 if (!$countonly) {
1070 $sql .= $this->db->order($sortfield, $sortorder);
1071 if (!empty($limit)) {
1072 $sql .= $this->db->plimit($limit + 1, $offset);
1073 }
1074 }
1075
1076 $resql = $this->db->query($sql);
1077 if ($resql) {
1078 if ($countonly) {
1079 $obj = $this->db->fetch_object($resql);
1080 if ($obj) {
1081 $num = $obj->nb;
1082 }
1083 } else {
1084 $num = $this->db->num_rows($resql);
1085
1086 $i = 0;
1087 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1088 $line = new BookKeepingLine($this->db);
1089
1090 $line->id = $obj->rowid;
1091
1092 $line->doc_date = $this->db->jdate($obj->doc_date);
1093 $line->doc_type = $obj->doc_type;
1094 $line->doc_ref = $obj->doc_ref;
1095 $line->fk_doc = $obj->fk_doc;
1096 $line->fk_docdet = $obj->fk_docdet;
1097 $line->thirdparty_code = $obj->thirdparty_code;
1098 $line->subledger_account = $obj->subledger_account;
1099 $line->subledger_label = $obj->subledger_label;
1100 $line->numero_compte = $obj->numero_compte;
1101 $line->label_compte = $obj->label_compte;
1102 $line->label_operation = $obj->label_operation;
1103 $line->debit = $obj->debit;
1104 $line->credit = $obj->credit;
1105 $line->montant = $obj->amount; // deprecated
1106 $line->amount = $obj->amount;
1107 $line->sens = $obj->sens;
1108 $line->multicurrency_amount = $obj->multicurrency_amount;
1109 $line->multicurrency_code = $obj->multicurrency_code;
1110 $line->lettering_code = $obj->lettering_code;
1111 $line->date_lettering = $this->db->jdate($obj->date_lettering);
1112 $line->fk_user_author = $obj->fk_user_author;
1113 $line->import_key = $obj->import_key;
1114 $line->code_journal = $obj->code_journal;
1115 $line->journal_label = $obj->journal_label;
1116 $line->piece_num = $obj->piece_num;
1117 $line->ref = $obj->ref;
1118 $line->date_creation = $this->db->jdate($obj->date_creation);
1119 $line->date_export = $this->db->jdate($obj->date_export);
1120 $line->date_validation = $this->db->jdate($obj->date_validation);
1121 // Due date
1122 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1123 $line->import_key = $obj->import_key;
1124
1125 $this->lines[] = $line;
1126
1127 $i++;
1128 }
1129 }
1130 $this->db->free($resql);
1131
1132 return $num;
1133 } else {
1134 $this->errors[] = 'Error '.$this->db->lasterror();
1135 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1136
1137 return -1;
1138 }
1139 }
1140
1153 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $showAlreadyExportMovements = 1)
1154 {
1155 global $conf;
1156
1157 dol_syslog(__METHOD__, LOG_DEBUG);
1158
1159 $sql = 'SELECT';
1160 $sql .= ' t.rowid,';
1161 $sql .= " t.doc_date,";
1162 $sql .= " t.doc_type,";
1163 $sql .= " t.doc_ref,";
1164 $sql .= " t.fk_doc,";
1165 $sql .= " t.fk_docdet,";
1166 $sql .= " t.thirdparty_code,";
1167 $sql .= " t.subledger_account,";
1168 $sql .= " t.subledger_label,";
1169 $sql .= " t.numero_compte,";
1170 $sql .= " t.label_compte,";
1171 $sql .= " t.label_operation,";
1172 $sql .= " t.debit,";
1173 $sql .= " t.credit,";
1174 $sql .= " t.lettering_code,";
1175 $sql .= " t.date_lettering,";
1176 $sql .= " t.montant as amount,";
1177 $sql .= " t.sens,";
1178 $sql .= " t.fk_user_author,";
1179 $sql .= " t.import_key,";
1180 $sql .= " t.code_journal,";
1181 $sql .= " t.journal_label,";
1182 $sql .= " t.piece_num,";
1183 $sql .= " t.date_creation,";
1184 $sql .= " t.date_lim_reglement,";
1185 $sql .= " t.tms as date_modification,";
1186 $sql .= " t.date_export,";
1187 $sql .= " t.date_validated as date_validation";
1188 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1189 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1190 if ($showAlreadyExportMovements == 0) {
1191 $sql .= " AND t.date_export IS NULL";
1192 }
1193
1194 // Manage filter
1195 if (is_array($filter)) { // deprecated, use $filter = USF syntax
1196 dol_syslog("You are using a deprecated use of fetchAll. filter parameter must be an USF string now.", LOG_WARNING);
1197 $sqlwhere = array();
1198 if (count($filter) > 0) {
1199 foreach ($filter as $key => $value) {
1200 if ($key == 't.doc_date') {
1201 $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate((int) $value)."'";
1202 } elseif ($key == 't.doc_date>=') {
1203 $sqlwhere[] = "t.doc_date >= '".$this->db->idate((int) $value)."'";
1204 } elseif ($key == 't.doc_date<=') {
1205 $sqlwhere[] = "t.doc_date <= '".$this->db->idate((int) $value)."'";
1206 } elseif ($key == 't.doc_date>') {
1207 $sqlwhere[] = "t.doc_date > '".$this->db->idate((int) $value)."'";
1208 } elseif ($key == 't.doc_date<') {
1209 $sqlwhere[] = "t.doc_date < '".$this->db->idate((int) $value)."'";
1210 } elseif ($key == 't.numero_compte>=') {
1211 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1212 } elseif ($key == 't.numero_compte<=') {
1213 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1214 } elseif ($key == 't.subledger_account>=') {
1215 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1216 } elseif ($key == 't.subledger_account<=') {
1217 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1218 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1219 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
1220 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1221 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1222 } elseif ($key == 't.date_creation>=') {
1223 $sqlwhere[] = "t.date_creation >= '".$this->db->idate((int) $value)."'";
1224 } elseif ($key == 't.date_creation<=') {
1225 $sqlwhere[] = "t.date_creation <= '".$this->db->idate((int) $value)."'";
1226 } elseif ($key == 't.tms>=') {
1227 $sqlwhere[] = "t.tms >= '".$this->db->idate((int) $value)."'";
1228 } elseif ($key == 't.tms<=') {
1229 $sqlwhere[] = "t.tms <= '".$this->db->idate((int) $value)."'";
1230 } elseif ($key == 't.date_export>=') {
1231 $sqlwhere[] = "t.date_export >= '".$this->db->idate((int) $value)."'";
1232 } elseif ($key == 't.date_export<=') {
1233 $sqlwhere[] = "t.date_export <= '".$this->db->idate((int) $value)."'";
1234 } elseif ($key == 't.date_validated>=') {
1235 $sqlwhere[] = "t.date_validated >= '".$this->db->idate((int) $value)."'";
1236 } elseif ($key == 't.date_validated<=') {
1237 $sqlwhere[] = "t.date_validated <= '".$this->db->idate((int) $value)."'";
1238 } elseif ($key == 't.credit' || $key == 't.debit') {
1239 $sqlwhere[] = natural_search($key, $value, 1, 1);
1240 } elseif ($key == 't.code_journal' && !empty($value)) {
1241 if (is_array($value)) {
1242 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1243 } else {
1244 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1245 }
1246 } elseif ($key == 't.reconciled_option') {
1247 $sqlwhere[] = 't.lettering_code IS NULL';
1248 } else {
1249 $sqlwhere[] = natural_search($key, $value, 0, 1);
1250 }
1251 }
1252 }
1253 if (count($sqlwhere) > 0) {
1254 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1255 }
1256
1257 $filter = '';
1258 }
1259
1260 // Manage filter
1261 $errormessage = '';
1262 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1263 if ($errormessage) {
1264 $this->errors[] = $errormessage;
1265 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1266 return -1;
1267 }
1268
1269 if (!empty($sortfield)) {
1270 $sql .= $this->db->order($sortfield, $sortorder);
1271 }
1272 if (!empty($limit)) {
1273 $sql .= $this->db->plimit($limit + 1, $offset);
1274 }
1275 $this->lines = array();
1276
1277 $resql = $this->db->query($sql);
1278 if ($resql) {
1279 $num = $this->db->num_rows($resql);
1280
1281 $i = 0;
1282 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1283 $line = new BookKeepingLine($this->db);
1284
1285 $line->id = $obj->rowid;
1286
1287 $line->doc_date = $this->db->jdate($obj->doc_date);
1288 $line->doc_type = $obj->doc_type;
1289 $line->doc_ref = $obj->doc_ref;
1290 $line->fk_doc = $obj->fk_doc;
1291 $line->fk_docdet = $obj->fk_docdet;
1292 $line->thirdparty_code = $obj->thirdparty_code;
1293 $line->subledger_account = $obj->subledger_account;
1294 $line->subledger_label = $obj->subledger_label;
1295 $line->numero_compte = $obj->numero_compte;
1296 $line->label_compte = $obj->label_compte;
1297 $line->label_operation = $obj->label_operation;
1298 $line->debit = $obj->debit;
1299 $line->credit = $obj->credit;
1300 $line->montant = $obj->amount; // deprecated
1301 $line->amount = $obj->amount;
1302 $line->sens = $obj->sens;
1303 $line->lettering_code = $obj->lettering_code;
1304 $line->date_lettering = $this->db->jdate($obj->date_lettering);
1305 $line->fk_user_author = $obj->fk_user_author;
1306 $line->import_key = $obj->import_key;
1307 $line->code_journal = $obj->code_journal;
1308 $line->journal_label = $obj->journal_label;
1309 $line->piece_num = $obj->piece_num;
1310 $line->date_creation = $this->db->jdate($obj->date_creation);
1311 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1312 $line->date_modification = $this->db->jdate($obj->date_modification);
1313 $line->date_export = $this->db->jdate($obj->date_export);
1314 $line->date_validation = $this->db->jdate($obj->date_validation);
1315
1316 $this->lines[] = $line;
1317
1318 $i++;
1319 }
1320 $this->db->free($resql);
1321
1322 return $num;
1323 } else {
1324 $this->errors[] = 'Error '.$this->db->lasterror();
1325 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1326 return -1;
1327 }
1328 }
1329
1342 public function fetchAllBalance($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $option = 0)
1343 {
1344 global $conf;
1345
1346 $this->lines = array();
1347
1348 dol_syslog(__METHOD__, LOG_DEBUG);
1349
1350 $sql = 'SELECT';
1351 $sql .= " t.numero_compte,";
1352 if (!empty($option)) {
1353 $sql .= " t.subledger_account,";
1354 $sql .= " t.subledger_label,";
1355 }
1356 $sql .= " SUM(t.debit) as debit,";
1357 $sql .= " SUM(t.credit) as credit";
1358 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1359 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1360
1361 // Manage filter
1362 if (is_array($filter)) {
1363 $sqlwhere = array();
1364 if (count($filter) > 0) {
1365 foreach ($filter as $key => $value) {
1366 if ($key == 't.doc_date') {
1367 $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate((int) $value)."'";
1368 } elseif ($key == 't.doc_date>=') {
1369 $sqlwhere[] = "t.doc_date >= '".$this->db->idate((int) $value)."'";
1370 } elseif ($key == 't.doc_date<=') {
1371 $sqlwhere[] = "t.doc_date <= '".$this->db->idate((int) $value)."'";
1372 } elseif ($key == 't.doc_date>') {
1373 $sqlwhere[] = "t.doc_date > '".$this->db->idate((int) $value)."'";
1374 } elseif ($key == 't.doc_date<') {
1375 $sqlwhere[] = "t.doc_date < '".$this->db->idate((int) $value)."'";
1376 } elseif ($key == 't.numero_compte>=') {
1377 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1378 } elseif ($key == 't.numero_compte<=') {
1379 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1380 } elseif ($key == 't.subledger_account>=') {
1381 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1382 } elseif ($key == 't.subledger_account<=') {
1383 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1384 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1385 $sqlwhere[] = $this->db->sanitize($key)." = ".((int) $value);
1386 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1387 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1388 } elseif ($key == 't.subledger_label') {
1389 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1390 } elseif ($key == 't.code_journal' && !empty($value)) {
1391 if (is_array($value)) {
1392 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1393 } else {
1394 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1395 }
1396 } elseif ($key == 't.reconciled_option') {
1397 $sqlwhere[] = 't.lettering_code IS NULL';
1398 } else {
1399 $sqlwhere[] = $this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
1400 }
1401 }
1402 }
1403 if (count($sqlwhere) > 0) {
1404 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1405 }
1406
1407 $filter = '';
1408 }
1409
1410 // Manage filter
1411 $errormessage = '';
1412 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1413 if ($errormessage) {
1414 $this->errors[] = $errormessage;
1415 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1416 return -1;
1417 }
1418
1419 if (!empty($option)) {
1420 $sql .= " AND t.subledger_account IS NOT NULL";
1421 $sql .= " AND t.subledger_account <> ''";
1422 $sql .= " GROUP BY t.numero_compte, t.subledger_account, t.subledger_label";
1423 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
1424 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
1425 } else {
1426 $sql .= ' GROUP BY t.numero_compte';
1427 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
1428 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
1429 }
1430
1431 if (!empty($sortfield)) {
1432 $sql .= $this->db->order($sortfield, $sortorder);
1433 }
1434 if (!empty($limit)) {
1435 $sql .= $this->db->plimit($limit + 1, $offset);
1436 }
1437
1438 //print $sql;
1439 $resql = $this->db->query($sql);
1440
1441 if ($resql) {
1442 $num = $this->db->num_rows($resql);
1443
1444 $i = 0;
1445 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1446 $line = new BookKeepingLine($this->db);
1447
1448 $line->numero_compte = $obj->numero_compte;
1449 //$line->label_compte = $obj->label_compte;
1450 if (!empty($option)) {
1451 $line->subledger_account = $obj->subledger_account;
1452 $line->subledger_label = $obj->subledger_label;
1453 }
1454 $line->debit = $obj->debit;
1455 $line->credit = $obj->credit;
1456
1457 $this->lines[] = $line;
1458
1459 $i++;
1460 }
1461 $this->db->free($resql);
1462
1463 return $num;
1464 } else {
1465 $this->errors[] = 'Error '.$this->db->lasterror();
1466 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1467
1468 return -1;
1469 }
1470 }
1471
1480 public function update(User $user, $notrigger = 0, $mode = '')
1481 {
1482 global $langs;
1483 $error = 0;
1484
1485 dol_syslog(__METHOD__, LOG_DEBUG);
1486
1487 // Clean parameters
1488 if (isset($this->doc_type)) {
1489 $this->doc_type = trim($this->doc_type);
1490 }
1491 if (isset($this->doc_ref)) {
1492 $this->doc_ref = trim($this->doc_ref);
1493 }
1494 if (isset($this->fk_doc)) {
1495 $this->fk_doc = (int) $this->fk_doc;
1496 }
1497 if (isset($this->fk_docdet)) {
1498 $this->fk_docdet = (int) $this->fk_docdet;
1499 }
1500 if (isset($this->thirdparty_code)) {
1501 $this->thirdparty_code = trim($this->thirdparty_code);
1502 }
1503 if (isset($this->subledger_account)) {
1504 $this->subledger_account = trim($this->subledger_account);
1505 }
1506 if (isset($this->subledger_label)) {
1507 $this->subledger_label = trim($this->subledger_label);
1508 }
1509 if (isset($this->numero_compte)) {
1510 $this->numero_compte = trim($this->numero_compte);
1511 }
1512 if (isset($this->label_compte)) {
1513 $this->label_compte = trim($this->label_compte);
1514 }
1515 if (isset($this->label_operation)) {
1516 $this->label_operation = trim($this->label_operation);
1517 }
1518 if (isset($this->sens)) {
1519 $this->sens = trim($this->sens);
1520 }
1521 if (isset($this->import_key)) {
1522 $this->import_key = trim($this->import_key);
1523 }
1524 if (isset($this->code_journal)) {
1525 $this->code_journal = trim($this->code_journal);
1526 }
1527 if (isset($this->journal_label)) {
1528 $this->journal_label = trim($this->journal_label);
1529 }
1530 if (isset($this->piece_num)) {
1531 $this->piece_num = (int) $this->piece_num;
1532 }
1533
1534 $result = $this->canModifyBookkeeping($this->id, $mode);
1535 if ($result < 0) {
1536 return -1;
1537 } elseif ($result == 0) {
1538 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1539 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1540 } else {
1541 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1542 }
1543 return -1;
1544 }
1545
1546 $this->debit = (float) price2num($this->debit, 'MT');
1547 $this->credit = (float) price2num($this->credit, 'MT');
1548 $this->montant = (float) price2num($this->montant, 'MT');
1549
1550 // Check parameters
1551 // Put here code to add a control on parameters values
1552
1553 // Update request
1554 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.$mode.' SET';
1555 $sql .= ' doc_date = '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'null').',';
1556 $sql .= ' doc_type = '.(isset($this->doc_type) ? "'".$this->db->escape($this->doc_type)."'" : "null").',';
1557 $sql .= ' doc_ref = '.(isset($this->doc_ref) ? "'".$this->db->escape($this->doc_ref)."'" : "null").',';
1558 $sql .= ' fk_doc = '.(isset($this->fk_doc) ? $this->fk_doc : "null").',';
1559 $sql .= ' fk_docdet = '.(isset($this->fk_docdet) ? $this->fk_docdet : "null").',';
1560 $sql .= ' thirdparty_code = '.(isset($this->thirdparty_code) ? "'".$this->db->escape($this->thirdparty_code)."'" : "null").',';
1561 $sql .= ' subledger_account = '.(isset($this->subledger_account) ? "'".$this->db->escape($this->subledger_account)."'" : "null").',';
1562 $sql .= ' subledger_label = '.(isset($this->subledger_label) ? "'".$this->db->escape($this->subledger_label)."'" : "null").',';
1563 $sql .= ' numero_compte = '.(isset($this->numero_compte) ? "'".$this->db->escape($this->numero_compte)."'" : "null").',';
1564 $sql .= ' label_compte = '.(isset($this->label_compte) ? "'".$this->db->escape($this->label_compte)."'" : "null").',';
1565 $sql .= ' label_operation = '.(isset($this->label_operation) ? "'".$this->db->escape($this->label_operation)."'" : "null").',';
1566 $sql .= ' debit = '.(isset($this->debit) ? $this->debit : "null").',';
1567 $sql .= ' credit = '.(isset($this->credit) ? $this->credit : "null").',';
1568 $sql .= ' montant = '.(isset($this->montant) ? $this->montant : "null").',';
1569 $sql .= ' sens = '.(isset($this->sens) ? "'".$this->db->escape($this->sens)."'" : "null").',';
1570 $sql .= ' fk_user_author = '.(isset($this->fk_user_author) ? $this->fk_user_author : "null").',';
1571 $sql .= ' import_key = '.(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").',';
1572 $sql .= ' code_journal = '.(isset($this->code_journal) ? "'".$this->db->escape($this->code_journal)."'" : "null").',';
1573 $sql .= ' journal_label = '.(isset($this->journal_label) ? "'".$this->db->escape($this->journal_label)."'" : "null").',';
1574 $sql .= ' piece_num = '.(isset($this->piece_num) ? $this->piece_num : "null");
1575 $sql .= ' WHERE rowid='.((int) $this->id);
1576
1577 $this->db->begin();
1578
1579 $resql = $this->db->query($sql);
1580 if (!$resql) {
1581 $error++;
1582 $this->errors[] = 'Error '.$this->db->lasterror();
1583 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1584 }
1585
1586 // Call triggers
1587 if (! $error && ! $notrigger) {
1588 $result = $this->call_trigger('BOOKKEEPING_MODIFY', $user);
1589 if ($result < 0) {
1590 $error++;
1591 }
1592 }
1593
1594 // Commit or rollback
1595 if ($error) {
1596 $this->db->rollback();
1597
1598 return -1 * $error;
1599 } else {
1600 $this->db->commit();
1601
1602 return 1;
1603 }
1604 }
1605
1615 public function updateByMvt($piece_num = '', $field = '', $value = '', $mode = '')
1616 {
1617 global $conf;
1618 $error = 0;
1619
1620 $sql_filter = $this->getCanModifyBookkeepingSQL();
1621 if (!isset($sql_filter)) {
1622 return -1;
1623 }
1624
1625 $this->db->begin();
1626
1627 $sql = "UPDATE ".$this->db->prefix().$this->table_element.$mode;
1628 $sql .= " SET ".$this->db->sanitize($field)." = ".(is_numeric($value) ? ((float) $value) : "'".$this->db->escape($value)."'");
1629 $sql .= " WHERE piece_num = ".((int) $piece_num);
1630 $sql .= " AND entity = " . ((int) $conf->entity);
1631 $sql .= $sql_filter;
1632
1633 $resql = $this->db->query($sql);
1634
1635 if (!$resql) {
1636 $error++;
1637 $this->errors[] = 'Error '.$this->db->lasterror();
1638 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1639 }
1640 if ($error) {
1641 $this->db->rollback();
1642
1643 return -1 * $error;
1644 } else {
1645 $this->db->commit();
1646
1647 return 1;
1648 }
1649 }
1650
1659 public function delete(User $user, $notrigger = 0, $mode = '')
1660 {
1661 global $langs;
1662
1663 dol_syslog(__METHOD__, LOG_DEBUG);
1664
1665 $result = $this->canModifyBookkeeping($this->id, $mode);
1666 if ($result < 0) {
1667 return -1;
1668 } elseif ($result == 0) {
1669 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1670 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1671 } else {
1672 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1673 }
1674 return -1;
1675 }
1676
1677 $error = 0;
1678
1679 $this->db->begin();
1680
1681 // Call triggers
1682 if (!$notrigger) {
1683 $result = $this->call_trigger('BOOKKEEPING_DELETE', $user);
1684 if ($result < 0) {
1685 $error++;
1686 }
1687 }
1688
1689 if (!$error) {
1690 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.$mode;
1691 $sql .= ' WHERE rowid='.((int) $this->id);
1692
1693 $resql = $this->db->query($sql);
1694 if (!$resql) {
1695 $error++;
1696 $this->errors[] = 'Error '.$this->db->lasterror();
1697 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1698 }
1699 }
1700
1701 // Commit or rollback
1702 if ($error) {
1703 $this->db->rollback();
1704
1705 return -1 * $error;
1706 } else {
1707 $this->db->commit();
1708
1709 return 1;
1710 }
1711 }
1712
1720 public function deleteByImportkey($importkey, $mode = '')
1721 {
1722 $this->db->begin();
1723
1724 $sql_filter = $this->getCanModifyBookkeepingSQL();
1725 if (!isset($sql_filter)) {
1726 return -1;
1727 }
1728
1729 // first check if line not yet in bookkeeping
1730 $sql = "DELETE";
1731 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1732 $sql .= " WHERE import_key = '".$this->db->escape($importkey)."'";
1733 $sql .= $sql_filter;
1734
1735 $resql = $this->db->query($sql);
1736
1737 if (!$resql) {
1738 $this->errors[] = "Error ".$this->db->lasterror();
1739 dol_syslog(get_class($this)."::delete Error ".$this->db->lasterror(), LOG_ERR);
1740 $this->db->rollback();
1741 return -1;
1742 }
1743
1744 $this->db->commit();
1745 return 1;
1746 }
1747
1757 public function deleteByYearAndJournal($delyear = 0, $journal = '', $mode = '', $delmonth = 0)
1758 {
1759 global $conf, $langs;
1760
1761 if (empty($delyear) && empty($journal)) {
1762 $this->error = 'ErrorOneFieldRequired';
1763 return -1;
1764 }
1765 if (!empty($delmonth) && empty($delyear)) {
1766 $this->error = 'YearRequiredIfMonthDefined';
1767 return -2;
1768 }
1769
1770 $sql_filter = $this->getCanModifyBookkeepingSQL();
1771 if (!isset($sql_filter)) {
1772 return -1;
1773 }
1774
1775 $this->db->begin();
1776
1777 // Delete record in bookkeeping
1778 $sql = "DELETE";
1779 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1780 $sql .= " WHERE 1 = 1";
1781 $sql .= dolSqlDateFilter('doc_date', 0, $delmonth, $delyear);
1782 if (!empty($journal)) {
1783 $sql .= " AND code_journal = '".$this->db->escape($journal)."'";
1784 }
1785 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1786 // Exclusion of validated entries at the time of deletion
1787 $sql .= " AND date_validated IS NULL";
1788 $sql .= $sql_filter;
1789
1790 // TODO: In a future we must forbid deletion if record is inside a closed fiscal period.
1791
1792 $resql = $this->db->query($sql);
1793
1794 if (!$resql) {
1795 $this->errors[] = "Error ".$this->db->lasterror();
1796 foreach ($this->errors as $errmsg) {
1797 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1798 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1799 }
1800 $this->db->rollback();
1801 return -1;
1802 }
1803
1804 $this->db->commit();
1805 return 1;
1806 }
1807
1816 public function deleteMvtNum($piecenum, $mode = '', $notrigger = 0)
1817 {
1818 global $conf, $user;
1819
1820 $sql_filter = $this->getCanModifyBookkeepingSQL();
1821 if (!isset($sql_filter)) {
1822 return -1;
1823 }
1824
1825 $nbprocessed = 0;
1826 $error = 0;
1827
1828 $this->db->begin();
1829
1830 // Call triggers
1831 if (! $error && ! $notrigger) {
1832 $result = $this->call_trigger('BOOKKEEPING_DELETE', $user);
1833 if ($result < 0) {
1834 $error++;
1835 }
1836 }
1837
1838 if (!$error) {
1839 // first check if line not yet in bookkeeping
1840 $sql = "DELETE";
1841 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1842 $sql .= " WHERE piece_num = ".(int) $piecenum;
1843 $sql .= " AND date_validated IS NULL"; // For security, exclusion of validated entries at the time of deletion
1844 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1845 $sql .= $sql_filter;
1846
1847 $resql = $this->db->query($sql);
1848
1849 if (!$resql) {
1850 $this->errors[] = "Error ".$this->db->lasterror();
1851 foreach ($this->errors as $errmsg) {
1852 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1853 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1854 }
1855 $this->db->rollback();
1856 return -1;
1857 } else {
1858 $nbprocessed = $this->db->affected_rows($resql);
1859 }
1860 }
1861
1862 // Commit or rollback
1863 if ($error) {
1864 $this->db->rollback();
1865 return -1 * $error;
1866 } else {
1867 $this->db->commit();
1868 return $nbprocessed;
1869 }
1870 }
1871
1879 public function createFromClone(User $user, $fromid)
1880 {
1881 dol_syslog(__METHOD__, LOG_DEBUG);
1882
1883 $error = 0;
1884 $object = new BookKeeping($this->db);
1885
1886 $this->db->begin();
1887
1888 // Load source object
1889 $object->fetch($fromid);
1890 // Reset object
1891 $object->id = 0;
1892
1893 // Clear fields
1894 // ...
1895
1896 // Create clone
1897 $object->context['createfromclone'] = 'createfromclone';
1898 $result = $object->create($user);
1899
1900 // Other options
1901 if ($result < 0) {
1902 $error++;
1903 $this->errors = $object->errors;
1904 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1905 }
1906
1907 unset($object->context['createfromclone']);
1908
1909 // End
1910 if (!$error) {
1911 $this->db->commit();
1912
1913 return $object->id;
1914 } else {
1915 $this->db->rollback();
1916
1917 return -1;
1918 }
1919 }
1920
1927 public function initAsSpecimen()
1928 {
1929 global $user;
1930
1931 $now = dol_now();
1932
1933 $this->id = 0;
1934 $this->doc_date = $now;
1935 $this->doc_type = '';
1936 $this->doc_ref = '';
1937 $this->fk_doc = 0;
1938 $this->fk_docdet = 0;
1939 $this->thirdparty_code = 'CU001';
1940 $this->subledger_account = '41100001';
1941 $this->subledger_label = 'My customer company';
1942 $this->numero_compte = '411';
1943 $this->label_compte = 'Customer';
1944 $this->label_operation = 'Sales of pea';
1945 $this->debit = 99.9;
1946 $this->credit = 0.0;
1947 $this->amount = 0.0;
1948 $this->sens = 'D';
1949 $this->fk_user_author = $user->id;
1950 $this->import_key = '20201027';
1951 $this->code_journal = 'VT';
1952 $this->journal_label = 'Journal de vente';
1953 $this->piece_num = 1234;
1954 $this->date_creation = $now;
1955
1956 return 1;
1957 }
1958
1966 public function fetchPerMvt($piecenum, $mode = '')
1967 {
1968 global $conf;
1969
1970 $sql = "SELECT piece_num, ref, doc_date, code_journal, journal_label, doc_ref, doc_type,";
1971 $sql .= " date_creation, tms as date_modification, date_validated as date_validation, date_lim_reglement, import_key";
1972 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1973 if ($mode != "_tmp") {
1974 $sql .= ", date_export";
1975 }
1976 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1977 $sql .= " WHERE piece_num = ".((int) $piecenum);
1978 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1979
1980 dol_syslog(__METHOD__, LOG_DEBUG);
1981 $result = $this->db->query($sql);
1982 if ($result) {
1983 $obj = $this->db->fetch_object($result);
1984
1985 $this->piece_num = $obj->piece_num;
1986 $this->ref = $obj->ref;
1987 $this->code_journal = $obj->code_journal;
1988 $this->journal_label = $obj->journal_label;
1989 $this->doc_date = $this->db->jdate($obj->doc_date);
1990 $this->doc_ref = $obj->doc_ref;
1991 $this->doc_type = $obj->doc_type;
1992 $this->date_creation = $this->db->jdate($obj->date_creation);
1993 $this->date_modification = $this->db->jdate($obj->date_modification);
1994 if ($mode != "_tmp") {
1995 $this->date_export = $this->db->jdate($obj->date_export);
1996 }
1997 $this->date_validation = $this->db->jdate($obj->date_validation);
1998 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1999 $this->import_key = $obj->import_key;
2000 } else {
2001 $this->error = "Error ".$this->db->lasterror();
2002 dol_syslog(__METHOD__.$this->error, LOG_ERR);
2003 return -1;
2004 }
2005
2006 return 1;
2007 }
2008
2015 public function getNextNumMvt($mode = '')
2016 {
2017 global $conf;
2018
2019 $sql = "SELECT MAX(piece_num) + 1 as max FROM ".$this->db->prefix().$this->table_element.$mode;
2020 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2021
2022 dol_syslog(get_class($this)."::getNextNumMvt", LOG_DEBUG);
2023
2024 $result = $this->db->query($sql);
2025
2026 if ($result) {
2027 $obj = $this->db->fetch_object($result);
2028 if ($obj) {
2029 $result = $obj->max;
2030 }
2031 if (empty($result)) {
2032 $result = 1;
2033 }
2034 return $result;
2035 } else {
2036 $this->error = "Error ".$this->db->lasterror();
2037 dol_syslog(get_class($this)."::getNextNumMvt ".$this->error, LOG_ERR);
2038 return -1;
2039 }
2040 }
2041
2048 public function getNextNumRef()
2049 {
2050 global $langs, $conf;
2051 $langs->load("accountancy");
2052
2053 if (getDolGlobalString('BOOKKEEPING_ADDON')) {
2054 $mybool = false;
2055
2056 $file = getDolGlobalString('BOOKKEEPING_ADDON') . ".php";
2057 $classname = getDolGlobalString('BOOKKEEPING_ADDON');
2058
2059 // Include file with class
2060 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2061 foreach ($dirmodels as $reldir) {
2062 $dir = dol_buildpath($reldir."core/modules/accountancy/");
2063
2064 // Load file with numbering class (if found)
2065 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2066 }
2067
2068 if (!$mybool) {
2069 dol_print_error(null, "Failed to include file ".$file);
2070 return '';
2071 }
2072
2073 $obj = new $classname();
2075 '@phan-var-force ModeleNumRefBookkeeping $obj';
2076
2077 $numref = $obj->getNextValue($this);
2078
2079 if ($numref != "") {
2080 return $numref;
2081 } else {
2082 $this->error = $obj->error;
2083 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
2084 return "";
2085 }
2086 } else {
2087 print $langs->trans("Error")." ".$langs->trans("Error_BOOKKEEPING_ADDON_NotDefined");
2088 return "";
2089 }
2090 }
2091
2092
2093
2101 public function fetchAllPerMvt($piecenum, $mode = '')
2102 {
2103 global $conf;
2104
2105 $sql = "SELECT rowid, doc_date, doc_type,";
2106 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
2107 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
2108 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
2109 $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
2110 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
2111 if ($mode != "_tmp") {
2112 $sql .= ", date_export";
2113 }
2114 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
2115 $sql .= " WHERE piece_num = ".((int) $piecenum);
2116 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2117
2118 dol_syslog(__METHOD__, LOG_DEBUG);
2119 $result = $this->db->query($sql);
2120 if ($result) {
2121 while ($obj = $this->db->fetch_object($result)) {
2122 $line = new BookKeepingLine($this->db);
2123
2124 $line->id = $obj->rowid;
2125
2126 $line->doc_date = $this->db->jdate($obj->doc_date);
2127 $line->doc_type = $obj->doc_type;
2128 $line->doc_ref = $obj->doc_ref;
2129 $line->fk_doc = $obj->fk_doc;
2130 $line->fk_docdet = $obj->fk_docdet;
2131 $line->thirdparty_code = $obj->thirdparty_code;
2132 $line->subledger_account = $obj->subledger_account;
2133 $line->subledger_label = $obj->subledger_label;
2134 $line->numero_compte = $obj->numero_compte;
2135 $line->label_compte = $obj->label_compte;
2136 $line->label_operation = $obj->label_operation;
2137 $line->debit = $obj->debit;
2138 $line->credit = $obj->credit;
2139 $line->montant = $obj->amount;
2140 $line->amount = $obj->amount;
2141 $line->sens = $obj->sens;
2142 $line->code_journal = $obj->code_journal;
2143 $line->journal_label = $obj->journal_label;
2144 $line->piece_num = $obj->piece_num;
2145 $line->date_creation = $obj->date_creation;
2146 $line->date_modification = $obj->date_modification;
2147 if ($mode != "_tmp") {
2148 $line->date_export = $obj->date_export;
2149 }
2150 $line->date_validation = $obj->date_validation;
2151
2152 $this->linesmvt[] = $line;
2153 }
2154 } else {
2155 $this->error = "Error ".$this->db->lasterror();
2156 dol_syslog(__METHOD__.$this->error, LOG_ERR);
2157 return -1;
2158 }
2159
2160 return 1;
2161 }
2162
2163 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2170 public function export_bookkeeping($model = 'ebp')
2171 {
2172 // phpcs:enable
2173 global $conf;
2174
2175 $sql = "SELECT rowid, doc_date, doc_type,";
2176 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
2177 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
2178 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, piece_num,";
2179 $sql .= " date_validated as date_validation";
2180 $sql .= " FROM ".$this->db->prefix().$this->table_element;
2181 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2182
2183 dol_syslog(get_class($this)."::export_bookkeeping", LOG_DEBUG);
2184
2185 $resql = $this->db->query($sql);
2186
2187 if ($resql) {
2188 $this->linesexport = array();
2189
2190 $num = $this->db->num_rows($resql);
2191 while ($obj = $this->db->fetch_object($resql)) {
2192 $line = new BookKeepingLine($this->db);
2193
2194 $line->id = $obj->rowid;
2195
2196 $line->doc_date = $this->db->jdate($obj->doc_date);
2197 $line->doc_type = $obj->doc_type;
2198 $line->doc_ref = $obj->doc_ref;
2199 $line->fk_doc = $obj->fk_doc;
2200 $line->fk_docdet = $obj->fk_docdet;
2201 $line->thirdparty_code = $obj->thirdparty_code;
2202 $line->subledger_account = $obj->subledger_account;
2203 $line->subledger_label = $obj->subledger_label;
2204 $line->numero_compte = $obj->numero_compte;
2205 $line->label_compte = $obj->label_compte;
2206 $line->label_operation = $obj->label_operation;
2207 $line->debit = $obj->debit;
2208 $line->credit = $obj->credit;
2209 $line->montant = $obj->amount;
2210 $line->amount = $obj->amount;
2211 $line->sens = $obj->sens;
2212 $line->code_journal = $obj->code_journal;
2213 $line->piece_num = $obj->piece_num;
2214 $line->date_validation = $obj->date_validation;
2215
2216 $this->linesexport[] = $line;
2217 }
2218 $this->db->free($resql);
2219
2220 return $num;
2221 } else {
2222 $this->error = "Error ".$this->db->lasterror();
2223 dol_syslog(get_class($this)."::export_bookkeeping ".$this->error, LOG_ERR);
2224 return -1;
2225 }
2226 }
2227
2235 public function transformTransaction($direction = 0, $piece_num = '')
2236 {
2237 global $conf;
2238
2239 $error = 0;
2240
2241 $sql_filter = $this->getCanModifyBookkeepingSQL();
2242
2243 if (!isset($sql_filter)) {
2244 return -1;
2245 }
2246
2247 $this->db->begin();
2248
2249 $tmpBookkeeping = new self($this->db);
2250 $tmpData = $this->db->getRow("SELECT doc_date, code_journal, ref FROM {$this->db->prefix()}accounting_bookkeeping_tmp WHERE piece_num = '{$this->db->escape($piece_num)}' AND entity = {$conf->entity}");
2251 $tmpBookkeeping->doc_date = $this->db->jdate($tmpData->doc_date);
2252 $tmpBookkeeping->code_journal = $tmpData->code_journal;
2253
2254 // Ref is copied from tmp only if defined => free num ref model has been used
2255 $ref = $tmpData->ref ?: $tmpBookkeeping->getNextNumRef();
2256 if ($direction == 0) {
2257 $next_piecenum = $this->getNextNumMvt();
2258 $now = dol_now();
2259
2260 if ($next_piecenum < 0) {
2261 $error++;
2262 }
2263
2264 if (!$error) {
2265 // Delete if there is an empty line
2266 $sql = 'DELETE FROM '.$this->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";
2267 $resql = $this->db->query($sql);
2268 if (!$resql) {
2269 $error++;
2270 $this->errors[] = 'Error '.$this->db->lasterror();
2271 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2272 }
2273 }
2274
2275 if (!$error) {
2276 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.' (doc_date, doc_type, ref,';
2277 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2278 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2279 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num, date_creation)';
2280 $sql .= ' SELECT doc_date, doc_type,' . "'{$ref}',";
2281 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2282 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2283 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, '.((int) $next_piecenum).", '".$this->db->idate($now)."'";
2284 $sql .= ' FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND numero_compte IS NOT NULL AND entity = ' .((int) $conf->entity);
2285 $sql .= $sql_filter;
2286 $resql = $this->db->query($sql);
2287 if (!$resql) {
2288 $error++;
2289 $this->errors[] = 'Error '.$this->db->lasterror();
2290 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2291 }
2292 }
2293
2294 if (!$error) {
2295 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2296 $resql = $this->db->query($sql);
2297 if (!$resql) {
2298 $error++;
2299 $this->errors[] = 'Error '.$this->db->lasterror();
2300 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2301 }
2302 }
2303 } elseif ($direction == 1) {
2304 if (!$error) {
2305 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2306 $resql = $this->db->query($sql);
2307 if (!$resql) {
2308 $error++;
2309 $this->errors[] = 'Error '.$this->db->lasterror();
2310 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2311 }
2312 }
2313
2314 if (!$error) {
2315 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.'_tmp (doc_date, doc_type, ref,';
2316 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2317 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2318 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num)';
2319 $sql .= ' SELECT doc_date, doc_type,' . "'{$ref}',";
2320 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2321 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2322 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num';
2323 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2324 $sql .= $sql_filter;
2325 $resql = $this->db->query($sql);
2326 if (!$resql) {
2327 $error++;
2328 $this->errors[] = 'Error '.$this->db->lasterror();
2329 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2330 }
2331 }
2332
2333 if (!$error) {
2334 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2335 $sql .= $sql_filter;
2336 $resql = $this->db->query($sql);
2337 if (!$resql) {
2338 $error++;
2339 $this->errors[] = 'Error '.$this->db->lasterror();
2340 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2341 }
2342 }
2343 }
2344 if (!$error) {
2345 $this->db->commit();
2346 return 1;
2347 } else {
2348 $this->db->rollback();
2349 return -1;
2350 }
2351 /*
2352 $sql = "DELETE FROM ";
2353 $sql .= " FROM " . $this->db->prefix() . "accounting_bookkeeping as ab";
2354 $sql .= " LEFT JOIN " . $this->db->prefix() . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2355 $sql .= " AND aa.active = 1";
2356 $sql .= " INNER JOIN " . $this->db->prefix() . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2357 $sql .= " AND asy.rowid = " . ((int) $pcgver);
2358 $sql .= " AND ab.entity IN (" . getEntity('accountancy') . ")";
2359 $sql .= " ORDER BY account_number ASC";
2360 */
2361 }
2362
2363 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2376 public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $aabase = '')
2377 {
2378 // phpcs:enable
2379 global $conf;
2380
2381 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
2382
2383 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2384
2385 $sql = "SELECT DISTINCT ab.numero_compte as account_number, aa.label as label, aa.rowid as rowid, aa.fk_pcg_version";
2386 $sql .= " FROM ".$this->db->prefix().$this->table_element." as ab";
2387 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as aa ON aa.account_number = ab.numero_compte";
2388 $sql .= " AND aa.active = 1";
2389 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2390 $sql .= " AND asy.rowid = ".((int) $pcgver);
2391 $sql .= " AND ab.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2392 $sql .= " ORDER BY account_number ASC";
2393
2394 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2395 $resql = $this->db->query($sql);
2396
2397 if (!$resql) {
2398 $this->error = "Error ".$this->db->lasterror();
2399 dol_syslog(get_class($this)."::select_account ".$this->error, LOG_ERR);
2400 return "-1";
2401 }
2402
2403 $out = ajax_combobox($htmlname, $event);
2404
2405 $options = array();
2406 $selected = 0;
2407
2408 while ($obj = $this->db->fetch_object($resql)) {
2409 $label = length_accountg($obj->account_number).' - '.$obj->label;
2410
2411 $select_value_in = $obj->rowid;
2412 $select_value_out = $obj->rowid;
2413
2414 if ($select_in == 1) {
2415 $select_value_in = $obj->account_number;
2416 }
2417 if ($select_out == 1) {
2418 $select_value_out = $obj->account_number;
2419 }
2420
2421 // Remember guy's we store in database llx_facturedet the rowid of accounting_account and not the account_number
2422 // Because same account_number can be share between different accounting_system and do have the same meaning
2423 if (($selectid != '') && $selectid == $select_value_in) {
2424 $selected = $select_value_out;
2425 }
2426
2427 $options[$select_value_out] = $label;
2428 }
2429
2430 $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300');
2431 $this->db->free($resql);
2432 return $out;
2433 }
2434
2442 public function getRootAccount($account = null)
2443 {
2444 global $conf;
2445 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2446
2447 $sql = "SELECT root.rowid, root.account_number, root.label as label,";
2448 $sql .= " parent.rowid as parent_rowid, parent.account_number as parent_account_number, parent.label as parent_label";
2449 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa";
2450 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2451 $sql .= " AND asy.rowid = ".((int) $pcgver);
2452 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as parent ON aa.account_parent = parent.rowid AND parent.active = 1";
2453 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as root ON parent.account_parent = root.rowid AND root.active = 1";
2454 $sql .= " WHERE aa.account_number = '".$this->db->escape($account)."'";
2455 $sql .= " AND aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2456
2457 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2458 $resql = $this->db->query($sql);
2459 if ($resql) {
2460 $obj = '';
2461 if ($this->db->num_rows($resql)) {
2462 $obj = $this->db->fetch_object($resql);
2463 }
2464
2465 $result = array('id' => $obj->rowid, 'account_number' => $obj->account_number, 'label' => $obj->label);
2466 return $result;
2467 } else {
2468 $this->error = "Error ".$this->db->lasterror();
2469 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2470
2471 return -1;
2472 }
2473 }
2474
2475 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2482 public function get_compte_desc($account = null)
2483 {
2484 // phpcs:enable
2485 global $conf;
2486
2487 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2488 $sql = "SELECT aa.account_number, aa.label, aa.rowid, aa.fk_pcg_version, cat.label as category";
2489 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa ";
2490 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2491 $sql .= " AND aa.account_number = '".$this->db->escape($account)."'";
2492 $sql .= " AND asy.rowid = ".((int) $pcgver);
2493 $sql .= " AND aa.active = 1";
2494 $sql .= " LEFT JOIN ".$this->db->prefix()."c_accounting_category as cat ON aa.fk_accounting_category = cat.rowid";
2495 $sql .= " WHERE aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2496
2497 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2498 $resql = $this->db->query($sql);
2499 if ($resql) {
2500 $obj = (object) array('label' => '');
2501 if ($this->db->num_rows($resql)) {
2502 $obj = $this->db->fetch_object($resql);
2503 }
2504 if (empty($obj->category)) {
2505 return $obj->label;
2506 } else {
2507 return $obj->label.' ('.$obj->category.')';
2508 }
2509 } else {
2510 $this->error = "Error ".$this->db->lasterror();
2511 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2512 return "-1";
2513 }
2514 }
2515
2523 public function getCanModifyBookkeepingSQL($alias = '', $force = false)
2524 {
2525 global $conf;
2526
2527 $alias = trim($alias);
2528 $alias = !empty($alias) && strpos($alias, '.') === false ? $alias . "." : $alias;
2529
2530 if (!isset(self::$can_modify_bookkeeping_sql_cached[$alias]) || $force) {
2531 $result = $this->loadFiscalPeriods($force, 'active');
2532 if ($result < 0) {
2533 return null;
2534 }
2535
2536 $sql_list = array();
2537 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2538 $i = 0;
2539 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2540 $sql_list[$i] = "(";
2541 $sql_list[$i] .= "'".$this->db->idate($fiscal_period['date_start']) . "' <= ".$this->db->sanitize($alias)."doc_date";
2542 if (!empty($fiscal_period['date_end'])) {
2543 $sql_list[$i] .= " AND ";
2544 $sql_list[$i] .= $this->db->sanitize($alias)."doc_date <= '" . $this->db->idate($fiscal_period['date_end'])."'";
2545 }
2546 $sql_list[$i] .= ")";
2547 $i++;
2548 }
2549 }
2550 $sqlsanitized = implode(' OR ', $sql_list);
2551 self::$can_modify_bookkeeping_sql_cached[$alias] = empty($sql_list) ? "" : " AND (".$sqlsanitized.")";
2552 }
2553
2554 return self::$can_modify_bookkeeping_sql_cached[$alias];
2555 }
2556
2564 public function canModifyBookkeeping($id, $mode = '')
2565 {
2566 global $conf;
2567
2568 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2569 $result = $this->loadFiscalPeriods(false, 'closed');
2570
2571 if ($result < 0) {
2572 return -1;
2573 }
2574
2575 $bookkeeping = new BookKeeping($this->db);
2576 $result = $bookkeeping->fetch($id, null, $mode);
2577 if ($result <= 0) {
2578 return $result;
2579 }
2580
2581 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2582 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2583 if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2584 return 0;
2585 }
2586 }
2587 }
2588
2589 return 1;
2590 } else {
2591 $result = $this->loadFiscalPeriods(false, 'active');
2592 if ($result < 0) {
2593 return -1;
2594 }
2595
2596 $bookkeeping = new BookKeeping($this->db);
2597 $result = $bookkeeping->fetch($id, null, $mode);
2598
2599 if ($result <= 0) {
2600 return $result;
2601 }
2602 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2603 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2604 if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $bookkeeping->doc_date && (empty($fiscal_period['date_end']) || $bookkeeping->doc_date <= $fiscal_period['date_end'])) {
2605 return 1;
2606 }
2607 }
2608 }
2609
2610 return 0;
2611 }
2612 }
2613
2626 public function accountingLabelForOperation($thirdpartyname, $reference, $labelaccount, $full = 0)
2627 {
2628 $accountingLabelOperation = '';
2629
2630 if (!getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER')) {
2631 $truncThirdpartyName = 16;
2632 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2633 if (empty($full)) {
2634 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2635 } else {
2636 $accountingLabelOperation = $thirdpartyname;
2637 }
2638 if (!empty($reference)) {
2639 $accountingLabelOperation .= ' - '. $reference;
2640 }
2641 if (!empty($labelaccount)) {
2642 $accountingLabelOperation .= ' - '. $labelaccount;
2643 }
2644 } elseif (getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 1) {
2645 $truncThirdpartyName = 32;
2646 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2647 if (empty($full)) {
2648 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2649 } else {
2650 $accountingLabelOperation = $thirdpartyname;
2651 }
2652 if (!empty($reference)) {
2653 $accountingLabelOperation .= ' - '. $reference;
2654 }
2655 } elseif (getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 2) {
2656 $truncThirdpartyName = 64;
2657 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2658 if (empty($full)) {
2659 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2660 } else {
2661 $accountingLabelOperation = $thirdpartyname;
2662 }
2663 }
2664
2665 return $accountingLabelOperation;
2666 }
2667
2674 public function validBookkeepingDate($date)
2675 {
2676 global $conf;
2677
2678 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2679 $result = $this->loadFiscalPeriods(false, 'closed');
2680
2681 if ($result < 0) {
2682 return -1;
2683 }
2684
2685 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2686 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2687 if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2688 return 0;
2689 }
2690 }
2691 }
2692
2693 return 1;
2694 } else {
2695 $result = $this->loadFiscalPeriods(false, 'active');
2696 if ($result < 0) {
2697 return -1;
2698 }
2699
2700 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2701 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2702 if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $date && (empty($fiscal_period['date_end']) || $date <= $fiscal_period['date_end'])) {
2703 return 1;
2704 }
2705 }
2706 }
2707
2708 return 0;
2709 }
2710 }
2711
2719 public function loadFiscalPeriods($force = false, $mode = 'active')
2720 {
2721 global $conf;
2722
2723 if ($mode == 'active') {
2724 if (!isset($conf->cache['active_fiscal_period_cached']) || $force) {
2725 $sql = "SELECT date_start, date_end";
2726 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2727 $sql .= " WHERE entity = " . ((int) $conf->entity);
2728 $sql .= " AND statut = 0";
2729
2730 $resql = $this->db->query($sql);
2731 if (!$resql) {
2732 $this->errors[] = $this->db->lasterror();
2733 return -1;
2734 }
2735
2736 $list = array();
2737 while ($obj = $this->db->fetch_object($resql)) {
2738 $date_start = $this->db->jdate($obj->date_start);
2739 $date_end_base = $this->db->jdate($obj->date_end);
2740 $date_end = dol_get_last_hour($date_end_base);
2741 $list[] = array(
2742 'date_start' => $date_start,
2743 'date_end' => $date_end,
2744 );
2745 }
2746 $conf->cache['active_fiscal_period_cached'] = $list;
2747 }
2748 }
2749 if ($mode == 'closed') {
2750 if (!isset($conf->cache['closed_fiscal_period_cached']) || $force) {
2751 $sql = "SELECT date_start, date_end";
2752 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2753 $sql .= " WHERE entity = " . ((int) $conf->entity);
2754 $sql .= " AND statut = 1";
2755
2756 $resql = $this->db->query($sql);
2757 if (!$resql) {
2758 $this->errors[] = $this->db->lasterror();
2759 return -1;
2760 }
2761
2762 $list = array();
2763 while ($obj = $this->db->fetch_object($resql)) {
2764 $date_start = $this->db->jdate($obj->date_start);
2765 $date_end_base = $this->db->jdate($obj->date_end);
2766 $date_end = dol_get_last_hour($date_end_base);
2767
2768 $list[] = array(
2769 'date_start' => $date_start,
2770 'date_end' => $date_end,
2771 );
2772 }
2773 $conf->cache['closed_fiscal_period_cached'] = $list;
2774 }
2775 }
2776
2777 return 1;
2778 }
2779
2785 public function getFiscalPeriods()
2786 {
2787 global $conf;
2788 $list = array();
2789
2790 $sql = "SELECT rowid, label, date_start, date_end, statut";
2791 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2792 $sql .= " WHERE entity = " . ((int) $conf->entity);
2793 $sql .= $this->db->order('date_start', 'ASC');
2794
2795 $resql = $this->db->query($sql);
2796 if (!$resql) {
2797 $this->errors[] = $this->db->lasterror();
2798 return -1;
2799 }
2800
2801 while ($obj = $this->db->fetch_object($resql)) {
2802 $list[$obj->rowid] = array(
2803 'id' => (int) $obj->rowid,
2804 'label' => $obj->label,
2805 'date_start' => $this->db->jdate($obj->date_start),
2806 'date_end' => $this->db->jdate($obj->date_end),
2807 'status' => (int) $obj->statut,
2808 );
2809 }
2810
2811 return $list;
2812 }
2813
2822 public function getCountByMonthForFiscalPeriod($date_start, $date_end)
2823 {
2824 global $conf;
2825
2826 $total = 0;
2827 $list = array();
2828
2829 $sql = "SELECT YEAR(b.doc_date) as year";
2830 for ($i = 1; $i <= 12; $i++) {
2831 $sql .= ", SUM(".$this->db->ifsql("MONTH(b.doc_date) = ".((int) $i), "1", "0") . ") AS month".((int) $i);
2832 }
2833 $sql .= ", COUNT(b.rowid) as total";
2834 $sql .= " FROM " . $this->db->prefix() . $this->table_element . " as b";
2835 $sql .= " WHERE b.doc_date >= '" . $this->db->idate($date_start) . "'";
2836 $sql .= " AND b.doc_date <= '" . $this->db->idate($date_end) . "'";
2837 $sql .= " AND b.entity IN (" . getEntity('bookkeeping', 0) . ")"; // We don't share object for accountancy
2838
2839 // Get count for each month into the fiscal period
2840 if (getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")) {
2841 // Loop on each closed period
2842 $sql .= " AND NOT EXISTS (SELECT rowid FROM ".MAIN_DB_PREFIX.'accounting_fiscalyear as af WHERE b.doc_date >= af.date_start AND b.doc_date <= af.date_end AND af.entity = '.((int) $conf->entity)." AND af.statut = 1)";
2843 } else {
2844 // Filter on the unitary flag/date lock on each record
2845 $sql .= " AND date_validated IS NULL"; // not locked
2846 }
2847
2848 $sql .= " GROUP BY YEAR(b.doc_date)";
2849 $sql .= $this->db->order("year", 'ASC');
2850
2851 dol_syslog(__METHOD__, LOG_DEBUG);
2852 $resql = $this->db->query($sql);
2853 if (!$resql) {
2854 $this->errors[] = $this->db->lasterror();
2855 return -1;
2856 }
2857
2858 while ($obj = $this->db->fetch_object($resql)) {
2859 $total += (int) $obj->total;
2860 $year_list = array(
2861 'year' => (int) $obj->year,
2862 'count' => array(),
2863 'total' => (int) $obj->total,
2864 );
2865 for ($i = 1; $i <= 12; $i++) {
2866 $year_list['count'][$i] = (int) $obj->{'month' . $i};
2867 }
2868
2869 $list[] = $year_list;
2870 }
2871
2872 $this->db->free($resql);
2873
2874 return array(
2875 'total' => $total,
2876 'list' => $list,
2877 );
2878 }
2879
2887 public function validateMovementForFiscalPeriod($date_start, $date_end)
2888 {
2889 global $conf;
2890
2891 $now = dol_now();
2892
2893 // Specify as export : update field date_validated on selected month/year
2894 $sql = " UPDATE " . $this->db->prefix() . $this->table_element;
2895 $sql .= " SET date_validated = '" . $this->db->idate($now) . "'";
2896 $sql .= " WHERE entity = " . ((int) $conf->entity);
2897 $sql .= " AND DATE(doc_date) >= '" . $this->db->idate($date_start) . "'";
2898 $sql .= " AND DATE(doc_date) <= '" . $this->db->idate($date_end) . "'";
2899 $sql .= " AND date_validated IS NULL";
2900
2901 dol_syslog(__METHOD__, LOG_DEBUG);
2902 $resql = $this->db->query($sql);
2903 if (!$resql) {
2904 $this->errors[] = $this->db->lasterror();
2905 return -1;
2906 }
2907
2908 return 1;
2909 }
2910
2918 public function accountingResult($date_start, $date_end)
2919 {
2920 global $conf;
2921
2922 $this->db->begin();
2923
2924 $income_statement_amount = 0;
2925
2926 if (getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT')) {
2927 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2928
2929 $pcg_type_filter = array();
2930 foreach ($accounting_groups_used_for_income_statement as $item) {
2931 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2932 }
2933
2934 $sql = 'SELECT';
2935 $sql .= " t.numero_compte,";
2936 $sql .= " aa.pcg_type,";
2937 $sql .= " (SUM(t.credit) - SUM(t.debit)) as accounting_result";
2938 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
2939 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2940 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2941 $sql .= " AND aa.entity = " . ((int) $conf->entity);
2942 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM ' . $this->db->prefix() . 'accounting_system WHERE rowid = ' . ((int) getDolGlobalInt('CHARTOFACCOUNTS')) . ')';
2943 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2944 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
2945 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
2946 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2947
2948 $resql = $this->db->query($sql);
2949 if (!$resql) {
2950 $this->errors[] = 'Error ' . $this->db->lasterror();
2951 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2952 } else {
2953 while ($obj = $this->db->fetch_object($resql)) {
2954 $income_statement_amount += $obj->accounting_result;
2955 }
2956 }
2957 }
2958
2959 return (string) $income_statement_amount;
2960 }
2961
2971 public function closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account = false, $generate_bookkeeping_records = true)
2972 {
2973 global $conf, $langs, $user;
2974
2975 // Current fiscal period
2976 $fiscal_period_id = max(0, $fiscal_period_id);
2977 if (empty($fiscal_period_id)) {
2978 $langs->load('errors');
2979 $this->errors[] = $langs->trans('ErrorBadParameters');
2980 return -1;
2981 }
2982 $fiscal_period = new Fiscalyear($this->db);
2983 $result = $fiscal_period->fetch($fiscal_period_id);
2984 if ($result < 0) {
2985 $this->error = $fiscal_period->error;
2986 $this->errors = $fiscal_period->errors;
2987 return -1;
2988 } elseif (empty($fiscal_period->id)) {
2989 $langs->loadLangs(array('errors', 'compta'));
2990 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2991 return -1;
2992 }
2993
2994 // New fiscal period
2995 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2996 if (empty($new_fiscal_period_id)) {
2997 $langs->load('errors');
2998 $this->errors[] = $langs->trans('ErrorBadParameters');
2999 return -1;
3000 }
3001 $new_fiscal_period = new Fiscalyear($this->db);
3002 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
3003 if ($result < 0) {
3004 $this->error = $new_fiscal_period->error;
3005 $this->errors = $new_fiscal_period->errors;
3006 return -1;
3007 } elseif (empty($new_fiscal_period->id)) {
3008 $langs->loadLangs(array('errors', 'compta'));
3009 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
3010 return -1;
3011 }
3012
3013 $error = 0;
3014 $this->db->begin();
3015
3016 $fiscal_period->statut = Fiscalyear::STATUS_CLOSED;
3017 $fiscal_period->status = Fiscalyear::STATUS_CLOSED; // Actually not used
3018 $result = $fiscal_period->update($user);
3019 if ($result < 0) {
3020 $this->error = $fiscal_period->error;
3021 $this->errors = $fiscal_period->errors;
3022 $error++;
3023 }
3024
3025 if (!$error && !empty($generate_bookkeeping_records)) {
3026 $journal_id = max(0, getDolGlobalString('ACCOUNTING_CLOSURE_DEFAULT_JOURNAL'));
3027 if (empty($journal_id)) {
3028 $langs->loadLangs(array('errors', 'accountancy'));
3029 $this->errors[] = $langs->trans('ErrorBadParameters') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
3030 $error++;
3031 }
3032
3033 // Fetch journal
3034 if (!$error) {
3035 $journal = new AccountingJournal($this->db);
3036 $result = $journal->fetch($journal_id);
3037 if ($result < 0) {
3038 $this->error = $journal->error;
3039 $this->errors = $journal->errors;
3040 $error++;
3041 } elseif ($result == 0) {
3042 $langs->loadLangs(array('errors', 'accountancy'));
3043 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
3044 $error++;
3045 }
3046 } else {
3047 $journal = null;
3048 }
3049
3050 if (!$error && is_object($journal)) {
3051 $accounting_groups_used_for_balance_sheet_account = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_BALANCE_SHEET_ACCOUNT'))), 'strlen');
3052 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
3053
3054 $pcg_type_filter = array();
3055 $tmp = array_merge($accounting_groups_used_for_balance_sheet_account, $accounting_groups_used_for_income_statement);
3056 foreach ($tmp as $item) {
3057 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
3058 }
3059
3060 $sql = 'SELECT';
3061 $sql .= " t.numero_compte,";
3062 if ($separate_auxiliary_account) {
3063 $sql .= " NULLIF(t.subledger_account, '') as subledger_account,"; // fix db issues with Null or "" values
3064 }
3065 $sql .= " aa.pcg_type,";
3066 $sql .= " (SUM(t.credit) - SUM(t.debit)) as opening_balance";
3067 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
3068 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
3069 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
3070 $sql .= " AND aa.entity = ". ((int) $conf->entity);
3071 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM '.$this->db->prefix().'accounting_system WHERE rowid = '.((int) getDolGlobalInt('CHARTOFACCOUNTS')).')';
3072 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
3073 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
3074 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
3075 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
3076 if ($separate_auxiliary_account) {
3077 $sql .= " , NULLIF(t.subledger_account, '')";
3078 }
3079 $sql .= ' HAVING (SUM(t.credit) - SUM(t.debit)) != 0 '; // Exclude rows with opening_balance = 0
3080 $sql .= $this->db->order("t.numero_compte", "ASC");
3081
3082 $resql = $this->db->query($sql);
3083 if (!$resql) {
3084 $this->errors[] = 'Error ' . $this->db->lasterror();
3085 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3086
3087 $error++;
3088 } else {
3089 $now = dol_now();
3090 $income_statement_amount = 0;
3091 while ($obj = $this->db->fetch_object($resql)) {
3092 if (in_array($obj->pcg_type, $accounting_groups_used_for_income_statement)) {
3093 $income_statement_amount += $obj->opening_balance;
3094 } else {
3095 // Insert bookkeeping record for balance sheet account
3096 $mt = $obj->opening_balance;
3097
3098 $bookkeeping = new BookKeeping($this->db);
3099 $bookkeeping->doc_date = $new_fiscal_period->date_start;
3100
3101 $bookkeeping->date_lim_reglement = '';
3102 $bookkeeping->doc_ref = $fiscal_period->label;
3103
3104 $bookkeeping->date_creation = $now;
3105 $bookkeeping->doc_type = 'closure';
3106 $bookkeeping->fk_doc = $fiscal_period->id;
3107 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3108 $bookkeeping->thirdparty_code = '';
3109
3110 if ($separate_auxiliary_account) {
3111 $bookkeeping->subledger_account = $obj->subledger_account;
3112 $sql = 'SELECT';
3113 $sql .= " subledger_label";
3114 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
3115 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
3116 $sql .= " ORDER BY doc_date DESC";
3117 $sql .= " LIMIT 1";
3118 $result = $this->db->query($sql);
3119 if (!$result) {
3120 $this->errors[] = 'Error: ' . $this->db->lasterror();
3121 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3122 $error++;
3123 }
3124 $objtmp = $this->db->fetch_object($result);
3125 $bookkeeping->subledger_label = $objtmp->subledger_label; // latest subledger label used
3126 } else {
3127 $bookkeeping->subledger_account = null;
3128 $bookkeeping->subledger_label = null;
3129 }
3130
3131 $bookkeeping->numero_compte = $obj->numero_compte;
3132 $accountingaccount = new AccountingAccount($this->db);
3133 $accountingaccount->fetch(0, $obj->numero_compte);
3134 $bookkeeping->label_compte = $accountingaccount->label; // latest account label used
3135
3136 $bookkeeping->label_operation = $new_fiscal_period->label;
3137 $bookkeeping->montant = $mt;
3138 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
3139 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
3140 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
3141 $bookkeeping->code_journal = $journal->code;
3142 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
3143 $bookkeeping->fk_user_author = $user->id;
3144 $bookkeeping->entity = $conf->entity;
3145
3146 $result = $bookkeeping->create($user);
3147 if ($result < 0) {
3148 $this->setErrorsFromObject($bookkeeping);
3149 $error++;
3150 break;
3151 }
3152 }
3153 }
3154
3155 // Insert bookkeeping record for income statement
3156 if (!$error && $income_statement_amount != 0) {
3157 $mt = $income_statement_amount;
3158 $accountingaccount = new AccountingAccount($this->db);
3159 $accountingaccount->fetch(0, getDolGlobalString($income_statement_amount < 0 ? 'ACCOUNTING_RESULT_LOSS' : 'ACCOUNTING_RESULT_PROFIT'), true);
3160
3161 $bookkeeping = new BookKeeping($this->db);
3162 $bookkeeping->doc_date = $new_fiscal_period->date_start;
3163
3164 $bookkeeping->date_lim_reglement = '';
3165 $bookkeeping->doc_ref = $fiscal_period->label;
3166
3167 $bookkeeping->date_creation = $now;
3168 $bookkeeping->doc_type = 'closure';
3169 $bookkeeping->fk_doc = $fiscal_period->id;
3170 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3171 $bookkeeping->thirdparty_code = '';
3172
3173 if ($separate_auxiliary_account) {
3174 $bookkeeping->subledger_account = $obj->subledger_account;
3175 $sql = 'SELECT';
3176 $sql .= " subledger_label";
3177 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
3178 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
3179 $sql .= " ORDER BY doc_date DESC";
3180 $sql .= " LIMIT 1";
3181 $result = $this->db->query($sql);
3182 if (!$result) {
3183 $this->errors[] = 'Error: ' . $this->db->lasterror();
3184 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3185 $error++;
3186 }
3187 $objtmp = $this->db->fetch_object($result);
3188 $bookkeeping->subledger_label = $objtmp->subledger_label ?? null; // latest subledger label used
3189 } else {
3190 $bookkeeping->subledger_account = null;
3191 $bookkeeping->subledger_label = null;
3192 }
3193
3194 $bookkeeping->numero_compte = $accountingaccount->account_number;
3195 $bookkeeping->label_compte = $accountingaccount->label;
3196
3197 $bookkeeping->label_operation = $new_fiscal_period->label;
3198 $bookkeeping->montant = $mt;
3199 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
3200 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
3201 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
3202 $bookkeeping->code_journal = $journal->code;
3203 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
3204 $bookkeeping->fk_user_author = $user->id;
3205 $bookkeeping->entity = $conf->entity;
3206
3207 $result = $bookkeeping->create($user);
3208 if ($result < 0) {
3209 $this->setErrorsFromObject($bookkeeping);
3210 $error++;
3211 }
3212 }
3213 $this->db->free($resql);
3214 }
3215 }
3216 }
3217
3218 if ($error) {
3219 $this->db->rollback();
3220 return -1;
3221 } else {
3222 $this->db->commit();
3223 return 1;
3224 }
3225 }
3226
3237 public function insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
3238 {
3239 global $conf, $langs, $user;
3240
3241 // Current fiscal period
3242 $fiscal_period_id = max(0, $fiscal_period_id);
3243 if (empty($fiscal_period_id)) {
3244 $langs->load('errors');
3245 $this->errors[] = $langs->trans('ErrorBadParameters');
3246 return -1;
3247 }
3248 $fiscal_period = new Fiscalyear($this->db);
3249 $result = $fiscal_period->fetch($fiscal_period_id);
3250 if ($result < 0) {
3251 $this->error = $fiscal_period->error;
3252 $this->errors = $fiscal_period->errors;
3253 return -1;
3254 } elseif (empty($fiscal_period->id)) {
3255 $langs->loadLangs(array('errors', 'compta'));
3256 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
3257 return -1;
3258 }
3259
3260 // New fiscal period
3261 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
3262 if (empty($new_fiscal_period_id)) {
3263 $langs->load('errors');
3264 $this->errors[] = $langs->trans('ErrorBadParameters');
3265 return -1;
3266 }
3267 $new_fiscal_period = new Fiscalyear($this->db);
3268 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
3269 if ($result < 0) {
3270 $this->error = $new_fiscal_period->error;
3271 $this->errors = $new_fiscal_period->errors;
3272 return -1;
3273 } elseif (empty($new_fiscal_period->id)) {
3274 $langs->loadLangs(array('errors', 'compta'));
3275 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
3276 return -1;
3277 }
3278
3279 // Inventory journal
3280 $inventory_journal_id = max(0, $inventory_journal_id);
3281 if (empty($inventory_journal_id)) {
3282 $langs->load('errors');
3283 $this->errors[] = $langs->trans('ErrorBadParameters');
3284 return -1;
3285 }
3286 // Fetch journal
3287 $inventory_journal = new AccountingJournal($this->db);
3288 $result = $inventory_journal->fetch($inventory_journal_id);
3289 if ($result < 0) {
3290 $this->error = $inventory_journal->error;
3291 $this->errors = $inventory_journal->errors;
3292 return -1;
3293 } elseif ($result == 0) {
3294 $langs->loadLangs(array('errors', 'accountancy'));
3295 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('InventoryJournal');
3296 return -1;
3297 }
3298
3299 $error = 0;
3300 $this->db->begin();
3301
3302 $sql = 'SELECT t.rowid';
3303 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
3304 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
3305 $sql .= " AND code_journal = '" . $this->db->escape($inventory_journal->code) . "'";
3306 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
3307 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
3308 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
3309 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
3310
3311 $resql = $this->db->query($sql);
3312 if (!$resql) {
3313 $this->errors[] = 'Error ' . $this->db->lasterror();
3314 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3315
3316 $error++;
3317 } else {
3318 $now = dol_now();
3319 while ($obj = $this->db->fetch_object($resql)) {
3320 $bookkeeping = new BookKeeping($this->db);
3321 $result = $bookkeeping->fetch($obj->rowid);
3322 if ($result < 0) {
3323 $this->error = $inventory_journal->error;
3324 $this->errors = $inventory_journal->errors;
3325 $error++;
3326 break;
3327 } elseif ($result == 0) {
3328 $langs->loadLangs(array('errors', 'accountancy'));
3329 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('LineId') . ': ' . $obj->rowid;
3330 $error++;
3331 break;
3332 }
3333
3334 $bookkeeping->id = 0;
3335 $bookkeeping->doc_date = $new_fiscal_period->date_start;
3336 $bookkeeping->doc_ref = $new_fiscal_period->label;
3337 $bookkeeping->date_creation = $now;
3338 $bookkeeping->doc_type = 'accounting_reversal';
3339 $bookkeeping->fk_doc = $new_fiscal_period->id;
3340 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3341
3342 $bookkeeping->montant = -$bookkeeping->montant;
3343 $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
3344 $old_debit = $bookkeeping->debit;
3345 $bookkeeping->debit = $bookkeeping->credit;
3346 $bookkeeping->credit = $old_debit;
3347
3348 $bookkeeping->fk_user_author = $user->id;
3349 $bookkeeping->entity = $conf->entity;
3350
3351 $result = $bookkeeping->create($user);
3352 if ($result < 0) {
3353 $this->error = $bookkeeping->error;
3354 $this->errors = $bookkeeping->errors;
3355 $error++;
3356 break;
3357 }
3358 }
3359 $this->db->free($resql);
3360 }
3361
3362 if ($error) {
3363 $this->db->rollback();
3364 return -1;
3365 } else {
3366 $this->db->commit();
3367 return 1;
3368 }
3369 }
3370
3378 public function assignAccountMass($toselect, $accounting_account = 0)
3379 {
3380 global $langs, $user;
3381
3382 $error = 0;
3383
3384 $this->db->begin();
3385
3386 $bookkeeping = new BookKeeping($this->db);
3387 $accountingaccount = new AccountingAccount($this->db);
3388 $nb = 0;
3389
3390 if ((int) $accounting_account > 0) {
3391 $accountingaccount->fetch($accounting_account);
3392 $echecT = [];
3393 foreach ($toselect as $id) {
3394 if ($bookkeeping->fetch($id)) {
3395 if ( !getDolGlobalString('ACCOUNTING_ACCOUNT_CUSTOMER')) {
3396 $accountcustcode = '411';
3397 } else $accountcustcode = getDolGlobalString('ACCOUNTING_ACCOUNT_CUSTOMER');
3398
3399 if ( !getDolGlobalString('ACCOUNTING_ACCOUNT_SUPPLIER')) {
3400 $accountsuppcode = '401';
3401 } else $accountsuppcode = getDolGlobalString('ACCOUNTING_ACCOUNT_SUPPLIER');
3402
3403 if (strpos($bookkeeping->numero_compte, $accountcustcode) === 0 || strpos($bookkeeping->numero_compte, $accountsuppcode) === 0) {
3404 $echecT[]=$bookkeeping->numero_compte;
3405 continue;
3406 }
3407
3408 $bookkeeping->numero_compte = $accountingaccount->account_number;
3409 $bookkeeping->label_compte = $accountingaccount->label;
3410
3411 $result = $bookkeeping->update($user);
3412
3413 if ($result > 0) {
3414 $nb++;
3415 } else {
3416 setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
3417 $error++;
3418 break;
3419 }
3420 }
3421 }
3422
3423 $echecImplode = implode(",", $echecT);
3424 } else {
3425 setEventMessages($langs->trans('NoAccountSelected'), null, 'errors');
3426 $error++;
3427 $this->db->rollback();
3428 }
3429
3430 if ($nb > 1) {
3431 setEventMessages($nb ." " . $langs->trans('AssignAccountsSuccess'), null, 'mesgs');
3432 } elseif ($nb > 0) {
3433 setEventMessages($nb ." " . $langs->trans('AssignAccountSuccess'), null, 'mesgs');
3434 } else {
3435 setEventMessages($langs->trans('AssignAccountError'), null, 'errors');
3436 $error++;
3437 }
3438
3439 if (!empty($echecImplode)) {
3440 $nbEchec = count(explode(',', $echecImplode));
3441 setEventMessages($nbEchec == 1 ? $langs->trans('NoAccountChangedWithAccountNumber') . ' ' . $echecImplode : $langs->trans('NoAccountsChangedWithAccountNumber') . ' ' . $echecImplode, null, 'errors'
3442 );
3443 }
3444
3445 if ($error) {
3446 $this->db->rollback();
3447 return -1;
3448 } else {
3449 $this->db->commit();
3450 return 1;
3451 }
3452 }
3453
3462 public function newClone($piecenum, $code_journal, $docdate)
3463 {
3464 global $langs;
3465
3466 $error = 0;
3467
3468 $accountingJournal = new AccountingJournal($this->db);
3469 $accountingJournal->fetch(0, $code_journal);
3470
3471 $bookKeepingValid = new BookKeeping($this->db);
3472
3473 $periodeFiscal = $bookKeepingValid->validBookkeepingDate($docdate);
3474 if ($periodeFiscal < 0) {
3475 $error++;
3476 return -1;
3477 } elseif ($periodeFiscal == 0) {
3478 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
3479 setEventMessages($langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod'), null, 'errors');
3480 } else {
3481 setEventMessages($langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod'), null, 'errors');
3482 header("Location: " . $_SERVER['HTTP_REFERER']);
3483 }
3484 $error++;
3485 return -1;
3486 }
3487
3488 $this->db->begin();
3489 $bookKeepingInstance = new BookKeeping($this->db);
3490 $pieceNumNext = $bookKeepingInstance->getNextNumMvt();
3491
3492 $cloneId = [];
3493 $sqlRowidClone = "SELECT rowid FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE piece_num = ".((int) $piecenum);
3494 $resqlRowidClone = $this->db->query($sqlRowidClone);
3495
3496 if ($resqlRowidClone) {
3497 while ($objRowidClone = $this->db->fetch_object($resqlRowidClone)) {
3498 $cloneId[] = $objRowidClone->rowid;
3499 }
3500
3501 foreach ($cloneId as $toselectid) {
3502 $bookKeeping = new BookKeeping($this->db);
3503 if ($bookKeeping->fetch($toselectid)) {
3504 $code_journal = getDolGlobalString('ACCOUNTING_CLONING_ENABLE_INPUT_JOURNAL') ? $code_journal : $bookKeeping->code_journal;
3505 $journal_label = getDolGlobalString('ACCOUNTING_CLONING_ENABLE_INPUT_JOURNAL') ? $accountingJournal->label : $bookKeeping->journal_label;
3506
3507 $sql = "SELECT piece_num, label_operation, numero_compte, label_compte, doc_type, code_journal, fk_user_author, doc_ref,";
3508 $sql .= " fk_doc, fk_docdet, debit, credit, journal_label, sens, montant, subledger_account, subledger_label";
3509 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping";
3510 $sql .= " WHERE rowid = " . ((int) $toselectid);
3511 $resql = $this->db->query($sql);
3512
3513 if ($resql) {
3514 while ($obj = $this->db->fetch_object($resql)) {
3515 $docRef = $langs->trans('CloneOf', $obj->doc_ref);
3516
3517 $sql_insert = "INSERT INTO " . MAIN_DB_PREFIX . "accounting_bookkeeping";
3518 $sql_insert .= " (piece_num, label_operation, numero_compte, label_compte, doc_type, code_journal, doc_date, fk_user_author, doc_ref,";
3519 $sql_insert .= " fk_doc, fk_docdet, debit, credit, date_creation, journal_label, sens, montant, subledger_account, subledger_label)";
3520 $sql_insert .= " VALUES";
3521 $sql_insert .= " (" . ((int) $pieceNumNext) . ", '" . $this->db->escape($obj->label_operation) . "', '" . $this->db->escape($obj->numero_compte) . "', '" . $this->db->escape($obj->label_compte) . "', '" . $this->db->escape($obj->doc_type) . "', '" . $this->db->escape($code_journal) . "', '" . $this->db->idate($docdate) . "', '" . $this->db->escape($obj->fk_user_author) . "', '" . $this->db->escape($docRef) . "', ";
3522 $sql_insert .= " ". ((int) $obj->fk_doc) . ", " . ((int) $obj->fk_docdet) . ", " . (float) $obj->debit . ", " . (float) $obj->credit . ", '" . $this->db->idate($docdate) . "', '" . $this->db->escape($journal_label) . "', '" . $this->db->escape($obj->sens) . "', " . (float) $obj->montant . ", '" . $this->db->escape($obj->subledger_account) . "', '" . $this->db->escape($obj->subledger_label) . "')";
3523
3524 $resqlInsert = $this->db->query($sql_insert);
3525
3526 if ($resqlInsert) {
3527 setEventMessages($langs->trans('CloningSuccess', $pieceNumNext), null, 'mesgs');
3528 } else {
3529 setEventMessages($langs->trans('CloningFailed') . $this->db->lasterror(), null, 'errors');
3530 $error++;
3531 }
3532 }
3533 }
3534 }
3535 }
3536 }
3537
3538 if ($error) {
3539 $this->db->rollback();
3540 return -1;
3541 } else {
3542 $this->db->commit();
3543 return 1;
3544 }
3545 }
3546
3555 public function newCloneMass($toselect, $code_journal, $docdate)
3556 {
3557 global $langs, $user;
3558
3559 $error = 0;
3560 $this->db->begin();
3561
3562 $now = dol_now();
3563 if (empty($docdate)) {
3564 $docdate = $now;
3565 }
3566
3567 $idImplodeSelect = implode(',', $toselect);
3568 $pieceNumT = [];
3569
3570 $sqlPieceNum = "SELECT DISTINCT(piece_num) FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE rowid IN (".$this->db->sanitize($idImplodeSelect).")";
3571 $resqlPieceNum = $this->db->query($sqlPieceNum);
3572
3573 if ($resqlPieceNum) {
3574 while ($objPieceNum = $this->db->fetch_object($resqlPieceNum)) {
3575 $pieceNumT[] = $objPieceNum->piece_num;
3576 }
3577
3578 foreach ($pieceNumT as $pieceNum) {
3579 $accountingJournal = new AccountingJournal($this->db);
3580 $accountingJournal->fetch(0, $code_journal);
3581 $bookKeepingValid = new BookKeeping($this->db);
3582 $periodeFiscal = $bookKeepingValid->validBookkeepingDate($docdate);
3583 if ($periodeFiscal < 0) {
3584 $error++;
3585 } elseif ($periodeFiscal == 0) {
3586 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
3587 setEventMessages($langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod'), null, 'errors');
3588 } else {
3589 setEventMessages($langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod'), null, 'errors');
3590 header("Location: " . $_SERVER['HTTP_REFERER']);
3591 }
3592 $error++;
3593 }
3594
3595 $bookKeepingInstance = new BookKeeping($this->db);
3596 $pieceNumNext = $bookKeepingInstance->getNextNumMvt();
3597 $cloneId = [];
3598 $sqlRowidClone = "SELECT rowid FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE piece_num = $pieceNum";
3599 $resqlRowidClone = $this->db->query($sqlRowidClone);
3600
3601 if ($resqlRowidClone) {
3602 while ($objRowidClone = $this->db->fetch_object($resqlRowidClone)) {
3603 $cloneId[] = $objRowidClone->rowid;
3604 }
3605
3606 foreach ($cloneId as $toselectid) {
3607 $bookKeeping = new BookKeeping($this->db);
3608 if ($bookKeeping->fetch($toselectid)) {
3609 $code_journal = getDolGlobalString('ACCOUNTING_CLONING_ENABLE_INPUT_JOURNAL') ? $code_journal : $bookKeeping->code_journal;
3610 $journal_label = getDolGlobalString('ACCOUNTING_CLONING_ENABLE_INPUT_JOURNAL') ? $accountingJournal->label : $bookKeeping->journal_label;
3611 $sql = "SELECT piece_num, label_operation, numero_compte, label_compte, subledger_account, subledger_label, doc_type, code_journal, fk_user_author, doc_ref, fk_doc, fk_docdet, debit, credit, journal_label, sens, montant";
3612 $sql .= " FROM ".$this->db->prefix()."accounting_bookkeeping WHERE rowid = " . ((int) $toselectid);
3613
3614 $resql = $this->db->query($sql);
3615 if ($resql) {
3616 while ($obj = $this->db->fetch_object($resql)) {
3617 $docRef = $langs->trans("CloneOf", $obj->doc_ref);
3618
3619 $sql_insert = "INSERT INTO ".$this->db->prefix()."accounting_bookkeeping (";
3620 $sql_insert .= " piece_num";
3621 $sql_insert .= ", label_operation";
3622 $sql_insert .= ", numero_compte";
3623 $sql_insert .= ", label_compte";
3624 $sql_insert .= ", subledger_account";
3625 $sql_insert .= ", subledger_label";
3626 $sql_insert .= ", doc_type";
3627 $sql_insert .= ", code_journal";
3628 $sql_insert .= ", doc_date";
3629 $sql_insert .= ", date_creation";
3630 $sql_insert .= ", fk_user_author";
3631 $sql_insert .= ", doc_ref";
3632 $sql_insert .= ", fk_doc";
3633 $sql_insert .= ", fk_docdet";
3634 $sql_insert .= ", debit";
3635 $sql_insert .= ", credit";
3636 $sql_insert .= ", journal_label";
3637 $sql_insert .= ", sens";
3638 $sql_insert .= ", montant";
3639 $sql_insert .= ")";
3640 $sql_insert .= " VALUES (";
3641 $sql_insert .= $pieceNumNext;
3642 $sql_insert .= ", '" . $this->db->escape($obj->label_operation) . "'";
3643 $sql_insert .= ", '" . $this->db->escape($obj->numero_compte) . "'";
3644 $sql_insert .= ", '" . $this->db->escape($obj->label_compte) . "'";
3645 $sql_insert .= ", '" . $this->db->escape($obj->subledger_account) . "'";
3646 $sql_insert .= ", '" . $this->db->escape($obj->subledger_label) . "'";
3647 $sql_insert .= ", ''";
3648 $sql_insert .= ", '" . $this->db->escape($code_journal) . "'";
3649 $sql_insert .= ", '" . $this->db->idate($docdate)."'";
3650 $sql_insert .= ", '" . $this->db->idate($now)."'";
3651 $sql_insert .= ", ".($user->id > 0 ? ((int) $user->id) : "NULL");
3652 $sql_insert .= ", '" . $this->db->escape($docRef) . "'";
3653 $sql_insert .= ", 0";
3654 $sql_insert .= ", 0";
3655 $sql_insert .= ", " . (float) $obj->debit;
3656 $sql_insert .= ", " . (float) $obj->credit;
3657 $sql_insert .= ", '" . $this->db->escape($journal_label) . "'";
3658 $sql_insert .= ", '" . $this->db->escape($obj->sens) . "'";
3659 $sql_insert .= ", " . (float) $obj->montant;
3660 $sql_insert .= ")";
3661
3662 $resqlInsert = $this->db->query($sql_insert);
3663
3664 if ($resqlInsert) {
3665 setEventMessages($langs->trans('CloningSuccess', $pieceNumNext), null, 'mesgs');
3666 } else {
3667 setEventMessages($langs->trans('CloningFailed'), null, 'errors');
3668 $error++;
3669 }
3670 }
3671 }
3672 }
3673 }
3674 }
3675 }
3676 }
3677
3678 if ($error) {
3679 $this->db->rollback();
3680 return -1;
3681 } else {
3682 $this->db->commit();
3683 return 1;
3684 }
3685 }
3686
3696 public function newReturnAccount(array $toselect, $code_journal, $docdate)
3697 {
3698 global $langs, $user;
3699
3700 $error = 0;
3701
3702 $now = dol_now();
3703 if (empty($docdate)) {
3704 $docdate = $now;
3705 }
3706
3707 $accountingJournal = new AccountingJournal($this->db);
3708 $accountingJournal->fetch(0, $code_journal);
3709
3710 $this->db->begin();
3711 $sqlAlreadyExtourne = "SELECT DISTINCT(piece_num) FROM " .MAIN_DB_PREFIX. "accounting_bookkeeping WHERE label_operation LIKE '%Extourne%'";
3712 $resqlAlreadyExtourne = $this->db->query($sqlAlreadyExtourne);
3713 $alreadyExtourneT = array();
3714 if ($resqlAlreadyExtourne) {
3715 while ($obj4 = $this->db->fetch_object($resqlAlreadyExtourne)) {
3716 $alreadyExtourneT []= $obj4->piece_num;
3717 }
3718 }
3719
3720 $idImplode = implode(',', $toselect);
3721 $sql1 = "SELECT DISTINCT(piece_num) from " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE rowid IN (".$this->db->sanitize($idImplode).")";
3722 $resql1 = $this->db->query($sql1);
3723 $pieceNumT = [];
3724
3725 if ($resql1) {
3726 while ($obj1 = $this->db->fetch_object($resql1)) {
3727 $pieceNumT [] = $obj1->piece_num;
3728 }
3729
3730 $i = mt_rand(0, 100);
3731 foreach ($pieceNumT as $pieceNum) {
3732 $newBookKeepingInstance = new BookKeeping($this->db);
3733 $pieceNumNext = $newBookKeepingInstance->getNextNumMvt();
3734 $extourneIds = [];
3735 $sql2 = "SELECT rowid FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE piece_num = ".((int) $pieceNum);
3736 $resql2 = $this->db->query($sql2);
3737
3738 if ($resql2) {
3739 while ($obj2 = $this->db->fetch_object($resql2)) {
3740 $extourneIds [] = $obj2->rowid;
3741 }
3742
3743 foreach ($extourneIds as $extourneId) {
3744 $newBookKeeping = new BookKeeping($this->db);
3745 $bookKeeping = new BookKeeping($this->db);
3746
3747 if ($bookKeeping->fetch($extourneId)) {
3748 if (in_array($bookKeeping->piece_num, $alreadyExtourneT)) {
3749 setEventMessages($langs->trans("AlreadyReturnedAccount", $bookKeeping->piece_num), null, 'errors');
3750 } else {
3751 $newBookKeeping->debit = $bookKeeping->credit;
3752 $newBookKeeping->credit = $bookKeeping->debit;
3753 if ($bookKeeping->sens == 'D') {
3754 $newBookKeeping->sens = 'C';
3755 } else {
3756 $newBookKeeping->sens = 'D';
3757 }
3758
3759 $newBookKeeping->label_operation = $langs->trans("ReturnAccount") . " " . $bookKeeping->piece_num . " - " . $bookKeeping->numero_compte . " - " . date('d/m/Y', dol_now()) . " - " . $i;
3760
3761 $newBookKeeping->numero_compte = $bookKeeping->numero_compte;
3762 $newBookKeeping->label_compte = $bookKeeping->label_compte;
3763 $newBookKeeping->doc_type = $bookKeeping->doc_type;
3764 $newBookKeeping->code_journal = $bookKeeping->code_journal;
3765 $newBookKeeping->doc_date = $docdate;
3766 $newBookKeeping->fk_user_author = $user->id;
3767 $newBookKeeping->doc_ref = $bookKeeping->doc_ref;
3768 $newBookKeeping->montant = $bookKeeping->montant;
3769 $newBookKeeping->journal_label = $bookKeeping->journal_label;
3770 $newBookKeeping->subledger_account = $bookKeeping->subledger_account;
3771 $newBookKeeping->subledger_label = $bookKeeping->subledger_label;
3772 }
3773 $createResult = $newBookKeeping->create($user);
3774
3775 if ($createResult >= 0) {
3776 $newBookKeeping->piece_num = $pieceNumNext;
3777 $newBookKeeping->fk_doc = $bookKeeping->fk_doc;
3778 $newBookKeeping->fk_docdet = $bookKeeping->fk_docdet;
3779 $newBookKeeping->update($user);
3780 setEventMessages($langs->trans("SuccessReturnedAccount", $bookKeeping->piece_num), null, 'mesgs');
3781 } else {
3782 setEventMessages($langs->trans("ErrorWhileCreating", $newBookKeeping->error), $newBookKeeping->errors, 'errors');
3783 $error++;
3784 }
3785 }
3786
3787 $i++;
3788 }
3789 }
3790 }
3791 }
3792
3793 if ($error) {
3794 $this->db->rollback();
3795 return -1;
3796 } else {
3797 $this->db->commit();
3798 return 1;
3799 }
3800 }
3801}
3802
3807{
3811 public $id;
3812
3816 public $doc_date = null;
3820 public $doc_type;
3821
3825 public $doc_ref;
3826
3830 public $fk_doc;
3831
3835 public $fk_docdet;
3836
3840 public $thirdparty_code;
3841
3845 public $subledger_account;
3846
3850 public $subledger_label;
3851
3855 public $numero_compte;
3856
3860 public $label_compte;
3861
3865 public $label_operation;
3866
3870 public $debit;
3871
3875 public $credit;
3876
3881 public $montant;
3882
3886 public $amount;
3887
3891 public $multicurrency_amount;
3892
3896 public $multicurrency_code;
3897
3901 public $sens;
3902
3906 public $lettering_code;
3907
3911 public $date_lettering;
3912
3916 public $fk_user_author;
3917
3918
3922 public $import_key;
3923
3927 public $code_journal;
3928
3932 public $journal_label;
3936 public $piece_num;
3937
3941 public $date_export;
3942
3946 public $date_lim_reglement;
3947
3951 public $code_tiers;
3952}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
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:475
$object ref
Definition info.php:90
Class to manage accounting accounts.
Class to manage accounting journals.
Class to manage Ledger (General Ledger and Subledger)
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.
fetchAllBalance($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND', $option=0)
Load object in memory from the 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.
getFiscalPeriods()
Get list of fiscal period ordered by start date.
getNextNumMvt($mode='')
Return next bookkeeping piece number.
newCloneMass($toselect, $code_journal, $docdate)
Mass clone.
get_compte_desc($account=null)
Description of accounting account.
accountingLabelForOperation($thirdpartyname, $reference, $labelaccount, $full=0)
Generate label operation when operation is transferred into accounting according to ACCOUNTING_LABEL_...
__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.
newReturnAccount(array $toselect, $code_journal, $docdate)
Mass ReturnAccount.
fetchPerMvt($piecenum, $mode='')
Load an accounting document into memory from database.
assignAccountMass($toselect, $accounting_account=0)
Mass account assignment.
create(User $user, $notrigger=0)
Create object into database.
updateByMvt($piece_num='', $field='', $value='', $mode='')
Update accounting movement.
createStd(User $user, $notrigger=0, $mode='')
Create object into database.
update(User $user, $notrigger=0, $mode='')
Update object into database.
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.
createFromValues($doc_date, $doc_ref, $doc_type, $fk_doc, $fk_docdet, $numero_compte, $label_compte, $label_operation, $amount, $code_journal, $journal_label, $subledger_account)
Create a line in database from values as parameters.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
deleteMvtNum($piecenum, $mode='', $notrigger=0)
Delete bookkeeping by piece number.
newClone($piecenum, $code_journal, $docdate)
Clone accounting entry.
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.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND', $showAlreadyExportMovements=1)
Load object in memory from the database.
accountingResult($date_start, $date_end)
Define accounting result.
canModifyBookkeeping($id, $mode='')
Is the bookkeeping can be modified or deleted ?
fetchAllPerMvt($piecenum, $mode='')
Load all accounting lines related to a given transaction ID $piecenum.
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,...
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
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:382
dol_get_last_hour($date, $gm='tzserver')
Return GMT time for last hour of a given GMT date (it replaces hours, min and second part to 23:59:59...
Definition date.lib.php:646
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
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...
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
global $dolibarr_main_demo
Definition index.php:57