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