dolibarr 21.0.3
bookkeeping.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2014-2017 Olivier Geffroy <jeff@jeffinfo.com>
3 * Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
4 * Copyright (C) 2015-2020 Florian Henry <florian.henry@open-concept.pro>
5 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2024 Jose MARTINEZ <jose.martinez@pichinov.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29// Class
30require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
32require_once DOL_DOCUMENT_ROOT.'/core/class/fiscalyear.class.php';
33require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
34require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
35
40{
44 public $element = 'accountingbookkeeping';
45
49 public $table_element = 'accounting_bookkeeping';
50
54 public $entity;
55
59 public $lines = array();
60
64 public $id;
65
69 public $doc_date;
70
74 public $date_lim_reglement;
75
79 public $doc_type;
80
84 public $doc_ref;
85
89 public $fk_doc;
90
94 public $fk_docdet;
95
99 public $thirdparty_code;
100
104 public $subledger_account;
105
109 public $subledger_label;
110
114 public $numero_compte;
115
119 public $label_compte;
120
124 public $label_operation;
125
129 public $debit;
130
134 public $credit;
135
140 public $montant;
141
146 public $amount;
147
151 public $sens;
152
156 public $fk_user_author;
157
161 public $import_key;
162
166 public $code_journal;
167
171 public $journal_label;
172
176 public $piece_num;
177
181 public $linesmvt = array();
182
186 public $linesexport = array();
187
191 public $date_export;
192
196 public $picto = 'generic';
197
201 public static $can_modify_bookkeeping_sql_cached;
202
203
209 public function __construct(DoliDB $db)
210 {
211 $this->db = $db;
212 }
213
221 public function create(User $user, $notrigger = 0)
222 {
223 global $conf, $langs;
224
225 dol_syslog(__METHOD__, LOG_DEBUG);
226
227 $error = 0;
228
229 // Clean parameters</center>
230 if (isset($this->doc_type)) {
231 $this->doc_type = trim($this->doc_type);
232 }
233 if (isset($this->doc_ref)) {
234 $this->doc_ref = trim($this->doc_ref);
235 $this->doc_ref = dol_trunc($this->doc_ref, 300); // We limit to 300 chars to avoid problems with too long ref in DB
236 }
237 if (isset($this->fk_doc)) {
238 $this->fk_doc = (int) $this->fk_doc;
239 }
240 if (isset($this->fk_docdet)) {
241 $this->fk_docdet = (int) $this->fk_docdet;
242 }
243 if (isset($this->thirdparty_code)) {
244 $this->thirdparty_code = trim($this->thirdparty_code);
245 }
246 if (isset($this->subledger_account)) {
247 $this->subledger_account = trim($this->subledger_account);
248 }
249 if (isset($this->subledger_label)) {
250 $this->subledger_label = trim($this->subledger_label);
251 }
252 if (isset($this->numero_compte)) {
253 $this->numero_compte = trim($this->numero_compte);
254 }
255 if (isset($this->label_compte)) {
256 $this->label_compte = trim($this->label_compte);
257 }
258 if (isset($this->label_operation)) {
259 $this->label_operation = trim($this->label_operation);
260 }
261 if (isset($this->debit)) {
262 $this->debit = (float) $this->debit;
263 }
264 if (isset($this->credit)) {
265 $this->credit = (float) $this->credit;
266 }
267 if (isset($this->montant)) {
268 $this->montant = (float) $this->montant;
269 }
270 if (isset($this->amount)) {
271 $this->amount = (float) $this->amount;
272 }
273 if (isset($this->sens)) {
274 $this->sens = trim($this->sens);
275 }
276 if (isset($this->import_key)) {
277 $this->import_key = trim($this->import_key);
278 }
279 if (isset($this->code_journal)) {
280 $this->code_journal = trim($this->code_journal);
281 }
282 if (isset($this->journal_label)) {
283 $this->journal_label = trim($this->journal_label);
284 }
285 if (isset($this->piece_num)) {
286 $this->piece_num = (int) $this->piece_num;
287 }
288 if (empty($this->debit)) {
289 $this->debit = 0.0;
290 }
291 if (empty($this->credit)) {
292 $this->credit = 0.0;
293 }
294
295 $result = $this->validBookkeepingDate($this->doc_date); // Check date according to ACCOUNTANCY_FISCAL_PERIOD_MODE.
296 if ($result < 0) {
297 return -1;
298 } elseif ($result == 0) {
299 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
300 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
301 } else {
302 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
303 }
304 return -1;
305 }
306
307 // Check parameters
308 if (($this->numero_compte == "") || $this->numero_compte == '-1' || $this->numero_compte == 'NotDefined') {
309 $langs->loadLangs(array("errors"));
310 if (in_array($this->doc_type, array('bank', 'expense_report'))) {
311 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet, $this->doc_type);
312 } else {
313 //$this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->doc_ref, $this->label_compte);
314 $mesg = $this->doc_ref.', '.$langs->trans("AccountAccounting").': '.($this->numero_compte != -1 ? $this->numero_compte : $langs->trans("Unknown"));
315 if ($this->subledger_account && $this->subledger_account != $this->numero_compte) {
316 $mesg .= ', '.$langs->trans("SubledgerAccount").': '.$this->subledger_account;
317 }
318 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForLine', $mesg);
319 }
320
321 return -1;
322 }
323
324 $this->db->begin();
325
326 $this->piece_num = 0;
327
328 // First check if line not yet already in bookkeeping.
329 // 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
330 // with same doc_type, fk_doc, numero_compte for 1 invoice line when using localtaxes with same account)
331 // WARNING: This is not reliable, label may have been modified. This is just a small protection.
332 // The page that make transfer make the test on couple (doc_type - fk_doc) only.
333 $sql = "SELECT count(*) as nb";
334 $sql .= " FROM ".$this->db->prefix().$this->table_element;
335 $sql .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'";
336 $sql .= " AND fk_doc = ".((int) $this->fk_doc);
337 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
338 // DO NOT USE THIS IN PRODUCTION. This will generate a lot of trouble into reports and will corrupt database (by generating duplicate entries.
339 $sql .= " AND fk_docdet = ".((int) $this->fk_docdet); // This field can be 0 if record is for several lines
340 }
341 $sql .= " AND numero_compte = '".$this->db->escape($this->numero_compte)."'";
342 $sql .= " AND label_operation = '".$this->db->escape($this->label_operation)."'";
343 if (!empty($this->subledger_account)) {
344 $sql .= " AND subledger_account = '".$this->db->escape($this->subledger_account)."'";
345 }
346 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
347
348 $resql = $this->db->query($sql);
349
350 if ($resql) {
351 $row = $this->db->fetch_object($resql);
352 if ($row->nb == 0) { // Not already into bookkeeping
353 // Check to know if piece_num already exists for data we try to insert to reuse the same value
354 $sqlnum = "SELECT piece_num";
355 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
356 $sqlnum .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'"; // For example doc_type = 'bank'
357 $sqlnum .= " AND fk_doc = ".((int) $this->fk_doc);
358 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
359 // fk_docdet is rowid into llx_bank or llx_facturedet or llx_facturefourndet, or ...
360 $sqlnum .= " AND fk_docdet = ".((int) $this->fk_docdet);
361 }
362 $sqlnum .= " AND doc_ref = '".$this->db->escape($this->doc_ref)."'"; // ref of source object
363 $sqlnum .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
364
365 dol_syslog(get_class($this).":: create sqlnum=".$sqlnum, LOG_DEBUG);
366 $resqlnum = $this->db->query($sqlnum);
367 if ($resqlnum) {
368 $num = $this->db->num_rows($resqlnum);
369 if ($num > 0) {
370 $objnum = $this->db->fetch_object($resqlnum);
371 $this->piece_num = $objnum->piece_num;
372 } else {
373 $this->piece_num = 0;
374 }
375 }
376
377 dol_syslog(get_class($this)."::create this->piece_num=".$this->piece_num, LOG_DEBUG);
378 if (empty($this->piece_num)) {
379 $sqlnum = "SELECT MAX(piece_num)+1 as maxpiecenum";
380 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
381 $sqlnum .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
382
383 $resqlnum = $this->db->query($sqlnum);
384 if ($resqlnum) {
385 $objnum = $this->db->fetch_object($resqlnum);
386 $this->piece_num = $objnum->maxpiecenum;
387 }
388 dol_syslog(get_class($this).":: create now this->piece_num=".$this->piece_num, LOG_DEBUG);
389 }
390 if (empty($this->piece_num)) {
391 $this->piece_num = 1;
392 }
393
394 $now = dol_now();
395
396 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element." (";
397 $sql .= "doc_date";
398 $sql .= ", date_lim_reglement";
399 $sql .= ", doc_type";
400 $sql .= ", doc_ref";
401 $sql .= ", fk_doc";
402 $sql .= ", fk_docdet";
403 $sql .= ", thirdparty_code";
404 $sql .= ", subledger_account";
405 $sql .= ", subledger_label";
406 $sql .= ", numero_compte";
407 $sql .= ", label_compte";
408 $sql .= ", label_operation";
409 $sql .= ", debit";
410 $sql .= ", credit";
411 $sql .= ", montant";
412 $sql .= ", sens";
413 $sql .= ", fk_user_author";
414 $sql .= ", date_creation";
415 $sql .= ", code_journal";
416 $sql .= ", journal_label";
417 $sql .= ", piece_num";
418 $sql .= ', entity';
419 $sql .= ") VALUES (";
420 $sql .= "'".$this->db->idate($this->doc_date)."'";
421 $sql .= ", ".(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL');
422 $sql .= ", '".$this->db->escape($this->doc_type)."'";
423 $sql .= ", '".$this->db->escape($this->doc_ref)."'";
424 $sql .= ", ".((int) $this->fk_doc);
425 $sql .= ", ".((int) $this->fk_docdet);
426 $sql .= ", ".(!empty($this->thirdparty_code) ? ("'".$this->db->escape($this->thirdparty_code)."'") : "NULL");
427 $sql .= ", ".(!empty($this->subledger_account) ? ("'".$this->db->escape($this->subledger_account)."'") : "NULL");
428 $sql .= ", ".(!empty($this->subledger_label) ? ("'".$this->db->escape($this->subledger_label)."'") : "NULL");
429 $sql .= ", '".$this->db->escape($this->numero_compte)."'";
430 $sql .= ", ".(!empty($this->label_compte) ? ("'".$this->db->escape($this->label_compte)."'") : "NULL");
431 $sql .= ", '".$this->db->escape($this->label_operation)."'";
432 $sql .= ", ".((float) $this->debit);
433 $sql .= ", ".((float) $this->credit);
434 $sql .= ", ".((float) $this->montant);
435 $sql .= ", ".(!empty($this->sens) ? ("'".$this->db->escape($this->sens)."'") : "NULL");
436 $sql .= ", '".$this->db->escape($this->fk_user_author)."'";
437 $sql .= ", '".$this->db->idate($now)."'";
438 $sql .= ", '".$this->db->escape($this->code_journal)."'";
439 $sql .= ", ".(!empty($this->journal_label) ? ("'".$this->db->escape($this->journal_label)."'") : "NULL");
440 $sql .= ", ".((int) $this->piece_num);
441 $sql .= ", ".(!isset($this->entity) ? $conf->entity : $this->entity);
442 $sql .= ")";
443
444 $resql = $this->db->query($sql);
445 if ($resql) {
446 $id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
447
448 if ($id > 0) {
449 $this->id = $id;
450 $result = 0;
451 } else {
452 $result = -2;
453 $error++;
454 $this->errors[] = 'Error Create Error '.$result.' lecture ID';
455 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
456 }
457 } else {
458 $result = -1;
459 $error++;
460 $this->errors[] = 'Error '.$this->db->lasterror();
461 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
462 }
463 } else { // Already exists
464 $result = -3;
465 $error++;
466 $this->error = 'BookkeepingRecordAlreadyExists';
467 dol_syslog(__METHOD__.' '.$this->error, LOG_WARNING);
468 }
469 } else {
470 $result = -5;
471 $error++;
472 $this->errors[] = 'Error '.$this->db->lasterror();
473 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
474 }
475
476 // Call triggers
477 if (! $error && ! $notrigger) {
478 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
479 if ($result < 0) {
480 $error++;
481 }
482 }
483
484 // Commit or rollback
485 if ($error) {
486 $this->db->rollback();
487 return -1 * $error;
488 } else {
489 $this->db->commit();
490 return $result;
491 }
492 }
493
504 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
505 {
506 global $db, $conf, $langs;
507 global $dolibarr_main_authentication, $dolibarr_main_demo;
508 global $menumanager, $hookmanager;
509
510 if (!empty($conf->dol_no_mouse_hover)) {
511 $notooltip = 1; // Force disable tooltips
512 }
513
514 $result = '';
515 $companylink = '';
516
517 $label = '<u>'.$langs->trans("Transaction").'</u>';
518 $label .= '<br>';
519 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->piece_num;
520
521 $url = DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?piece_num='.$this->piece_num;
522
523 if ($option != 'nolink') {
524 // Add param to save lastsearch_values or not
525 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
526 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
527 $add_save_lastsearch_values = 1;
528 }
529 if ($add_save_lastsearch_values) {
530 $url .= '&save_lastsearch_values=1';
531 }
532 }
533
534 $linkclose = '';
535 if (empty($notooltip)) {
536 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
537 $label = $langs->trans("ShowTransaction");
538 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
539 }
540 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
541 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
542 } else {
543 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
544 }
545
546 $linkstart = '<a href="'.$url.'"';
547 $linkstart .= $linkclose.'>';
548 $linkend = '</a>';
549
550 $result .= $linkstart;
551 if ($withpicto) {
552 $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);
553 }
554 if ($withpicto != 2) {
555 $result .= $this->piece_num;
556 }
557 $result .= $linkend;
558 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
559
560 global $action;
561 $hookmanager->initHooks(array($this->element . 'dao'));
562 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
563 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
564 if ($reshook > 0) {
565 $result = $hookmanager->resPrint;
566 } else {
567 $result .= $hookmanager->resPrint;
568 }
569 return $result;
570 }
571
580 public function createStd(User $user, $notrigger = 0, $mode = '')
581 {
582 global $conf, $langs;
583
584 $langs->loadLangs(array("accountancy", "bills", "compta"));
585
586 dol_syslog(__METHOD__, LOG_DEBUG);
587
588 $error = 0;
589
590 // Clean parameters
591 if (isset($this->doc_type)) {
592 $this->doc_type = trim($this->doc_type);
593 }
594 if (isset($this->doc_ref)) {
595 $this->doc_ref = trim($this->doc_ref);
596 }
597 if (isset($this->fk_doc)) {
598 $this->fk_doc = (int) $this->fk_doc;
599 }
600 if (isset($this->fk_docdet)) {
601 $this->fk_docdet = (int) $this->fk_docdet;
602 }
603 if (isset($this->thirdparty_code)) {
604 $this->thirdparty_code = trim($this->thirdparty_code);
605 }
606 if (isset($this->subledger_account)) {
607 $this->subledger_account = trim($this->subledger_account);
608 }
609 if (isset($this->subledger_label)) {
610 $this->subledger_label = trim($this->subledger_label);
611 }
612 if (isset($this->numero_compte)) {
613 $this->numero_compte = trim($this->numero_compte);
614 }
615 if (isset($this->label_compte)) {
616 $this->label_compte = trim($this->label_compte);
617 }
618 if (isset($this->label_operation)) {
619 $this->label_operation = trim($this->label_operation);
620 }
621 if (isset($this->sens)) {
622 $this->sens = trim($this->sens);
623 }
624 if (isset($this->import_key)) {
625 $this->import_key = trim($this->import_key);
626 }
627 if (isset($this->code_journal)) {
628 $this->code_journal = trim($this->code_journal);
629 }
630 if (isset($this->journal_label)) {
631 $this->journal_label = trim($this->journal_label);
632 }
633 if (isset($this->piece_num)) {
634 $this->piece_num = (int) $this->piece_num;
635 }
636 if (empty($this->debit)) {
637 $this->debit = 0;
638 }
639 if (empty($this->credit)) {
640 $this->credit = 0;
641 }
642 if (empty($this->montant)) {
643 $this->montant = 0;
644 }
645
646 $result = $this->validBookkeepingDate($this->doc_date);
647 if ($result < 0) {
648 return -1;
649 } elseif ($result == 0) {
650 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
651 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
652 } else {
653 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
654 }
655 return -1;
656 }
657
658 $this->debit = (float) price2num($this->debit, 'MT');
659 $this->credit = (float) price2num($this->credit, 'MT');
660 $this->montant = (float) price2num($this->montant, 'MT');
661
662 $now = dol_now();
663
664 // Check parameters
665 $this->journal_label = $langs->trans($this->journal_label);
666
667 // Insert request
668 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.$mode.' (';
669 $sql .= 'doc_date,';
670 $sql .= 'date_lim_reglement,';
671 $sql .= 'doc_type,';
672 $sql .= 'doc_ref,';
673 $sql .= 'fk_doc,';
674 $sql .= 'fk_docdet,';
675 $sql .= 'thirdparty_code,';
676 $sql .= 'subledger_account,';
677 $sql .= 'subledger_label,';
678 $sql .= 'numero_compte,';
679 $sql .= 'label_compte,';
680 $sql .= 'label_operation,';
681 $sql .= 'debit,';
682 $sql .= 'credit,';
683 $sql .= 'montant,';
684 $sql .= 'sens,';
685 $sql .= 'fk_user_author,';
686 $sql .= 'date_creation,';
687 $sql .= 'code_journal,';
688 $sql .= 'journal_label,';
689 $sql .= 'piece_num,';
690 $sql .= 'entity';
691 $sql .= ') VALUES (';
692 $sql .= ' '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'NULL').',';
693 $sql .= ' '.(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL').',';
694 $sql .= ' '.(!isset($this->doc_type) ? 'NULL' : "'".$this->db->escape($this->doc_type)."'").',';
695 $sql .= ' '.(!isset($this->doc_ref) ? 'NULL' : "'".$this->db->escape($this->doc_ref)."'").',';
696 $sql .= ' '.(empty($this->fk_doc) ? '0' : (int) $this->fk_doc).',';
697 $sql .= ' '.(empty($this->fk_docdet) ? '0' : (int) $this->fk_docdet).',';
698 $sql .= ' '.(!isset($this->thirdparty_code) ? 'NULL' : "'".$this->db->escape($this->thirdparty_code)."'").',';
699 $sql .= ' '.(!isset($this->subledger_account) ? 'NULL' : "'".$this->db->escape($this->subledger_account)."'").',';
700 $sql .= ' '.(!isset($this->subledger_label) ? 'NULL' : "'".$this->db->escape($this->subledger_label)."'").',';
701 $sql .= ' '.(!isset($this->numero_compte) ? 'NULL' : "'".$this->db->escape($this->numero_compte)."'").',';
702 $sql .= ' '.(!isset($this->label_compte) ? 'NULL' : "'".$this->db->escape($this->label_compte)."'").',';
703 $sql .= ' '.(!isset($this->label_operation) ? 'NULL' : "'".$this->db->escape($this->label_operation)."'").',';
704 $sql .= ' '.(!isset($this->debit) ? 'NULL' : $this->debit).',';
705 $sql .= ' '.(!isset($this->credit) ? 'NULL' : $this->credit).',';
706 $sql .= ' '.(!isset($this->montant) ? 'NULL' : $this->montant).',';
707 $sql .= ' '.(!isset($this->sens) ? 'NULL' : "'".$this->db->escape($this->sens)."'").',';
708 $sql .= ' '.((int) $user->id).',';
709 $sql .= ' '."'".$this->db->idate($now)."',";
710 $sql .= ' '.(empty($this->code_journal) ? 'NULL' : "'".$this->db->escape($this->code_journal)."'").',';
711 $sql .= ' '.(empty($this->journal_label) ? 'NULL' : "'".$this->db->escape($this->journal_label)."'").',';
712 $sql .= ' '.(empty($this->piece_num) ? 'NULL' : $this->db->escape($this->piece_num)).',';
713 $sql .= ' '.(!isset($this->entity) ? $conf->entity : $this->entity);
714 $sql .= ')';
715
716 $this->db->begin();
717
718 $resql = $this->db->query($sql);
719 if (!$resql) {
720 $error++;
721 $this->errors[] = 'Error '.$this->db->lasterror();
722 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
723 }
724
725 if (!$error) {
726 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element.$mode);
727 // Call triggers
728 if (! $notrigger) {
729 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
730 if ($result < 0) {
731 $error++;
732 }
733 }
734 }
735
736 // Commit or rollback
737 if ($error) {
738 $this->db->rollback();
739
740 return -1 * $error;
741 } else {
742 $this->db->commit();
743
744 return $this->id;
745 }
746 }
747
756 public function fetch($id, $ref = null, $mode = '')
757 {
758 global $conf;
759
760 dol_syslog(__METHOD__, LOG_DEBUG);
761
762 $sql = 'SELECT';
763 $sql .= ' t.rowid,';
764 $sql .= " t.doc_date,";
765 $sql .= " t.date_lim_reglement,";
766 $sql .= " t.doc_type,";
767 $sql .= " t.doc_ref,";
768 $sql .= " t.fk_doc,";
769 $sql .= " t.fk_docdet,";
770 $sql .= " t.thirdparty_code,";
771 $sql .= " t.subledger_account,";
772 $sql .= " t.subledger_label,";
773 $sql .= " t.numero_compte,";
774 $sql .= " t.label_compte,";
775 $sql .= " t.label_operation,";
776 $sql .= " t.debit,";
777 $sql .= " t.credit,";
778 $sql .= " t.montant as amount,";
779 $sql .= " t.sens,";
780 $sql .= " t.fk_user_author,";
781 $sql .= " t.import_key,";
782 $sql .= " t.code_journal,";
783 $sql .= " t.journal_label,";
784 $sql .= " t.piece_num,";
785 $sql .= " t.date_creation,";
786 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
787 if ($mode != "_tmp") {
788 $sql .= " t.date_export,";
789 }
790 $sql .= " t.date_validated as date_validation";
791 $sql .= ' FROM '.$this->db->prefix().$this->table_element.$mode.' as t';
792 $sql .= ' WHERE 1 = 1';
793 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
794 if (null !== $ref) {
795 $sql .= " AND t.rowid = ".((int) $ref);
796 } else {
797 $sql .= " AND t.rowid = ".((int) $id);
798 }
799
800 $resql = $this->db->query($sql);
801 if ($resql) {
802 $numrows = $this->db->num_rows($resql);
803 if ($numrows) {
804 $obj = $this->db->fetch_object($resql);
805
806 $this->id = $obj->rowid;
807
808 $this->doc_date = $this->db->jdate($obj->doc_date);
809 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
810 $this->doc_type = $obj->doc_type;
811 $this->doc_ref = $obj->doc_ref;
812 $this->fk_doc = $obj->fk_doc;
813 $this->fk_docdet = $obj->fk_docdet;
814 $this->thirdparty_code = $obj->thirdparty_code;
815 $this->subledger_account = $obj->subledger_account;
816 $this->subledger_label = $obj->subledger_label;
817 $this->numero_compte = $obj->numero_compte;
818 $this->label_compte = $obj->label_compte;
819 $this->label_operation = $obj->label_operation;
820 $this->debit = $obj->debit;
821 $this->credit = $obj->credit;
822 $this->montant = $obj->amount;
823 $this->amount = $obj->amount;
824 $this->sens = $obj->sens;
825 $this->fk_user_author = $obj->fk_user_author;
826 $this->import_key = $obj->import_key;
827 $this->code_journal = $obj->code_journal;
828 $this->journal_label = $obj->journal_label;
829 $this->piece_num = $obj->piece_num;
830 $this->date_creation = $this->db->jdate($obj->date_creation);
831 $this->date_export = $this->db->jdate($obj->date_export);
832 $this->date_validation = isset($obj->date_validation) ? $this->db->jdate($obj->date_validation) : '';
833 }
834 $this->db->free($resql);
835
836 if ($numrows) {
837 return 1;
838 } else {
839 return 0;
840 }
841 } else {
842 $this->errors[] = 'Error '.$this->db->lasterror();
843 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
844
845 return -1;
846 }
847 }
848
849
863 public function fetchAllByAccount($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0, $countonly = 0)
864 {
865 global $conf;
866
867 dol_syslog(__METHOD__, LOG_DEBUG);
868
869 $this->lines = array();
870 $num = 0;
871
872 $sql = 'SELECT';
873 if ($countonly) {
874 $sql .= ' COUNT(t.rowid) as nb';
875 } else {
876 $sql .= ' t.rowid,';
877 $sql .= " t.doc_date,";
878 $sql .= " t.doc_type,";
879 $sql .= " t.doc_ref,";
880 $sql .= " t.fk_doc,";
881 $sql .= " t.fk_docdet,";
882 $sql .= " t.thirdparty_code,";
883 $sql .= " t.subledger_account,";
884 $sql .= " t.subledger_label,";
885 $sql .= " t.numero_compte,";
886 $sql .= " t.label_compte,";
887 $sql .= " t.label_operation,";
888 $sql .= " t.debit,";
889 $sql .= " t.credit,";
890 $sql .= " t.montant as amount,";
891 $sql .= " t.sens,";
892 $sql .= " t.multicurrency_amount,";
893 $sql .= " t.multicurrency_code,";
894 $sql .= " t.lettering_code,";
895 $sql .= " t.date_lettering,";
896 $sql .= " t.fk_user_author,";
897 $sql .= " t.import_key,";
898 $sql .= " t.code_journal,";
899 $sql .= " t.journal_label,";
900 $sql .= " t.piece_num,";
901 $sql .= " t.date_creation,";
902 $sql .= " t.date_export,";
903 $sql .= " t.date_validated as date_validation,";
904 $sql .= " t.date_lim_reglement,";
905 $sql .= " t.import_key";
906 }
907 // Manage filter
908 $sqlwhere = array();
909 if (count($filter) > 0) {
910 foreach ($filter as $key => $value) {
911 if ($key == 't.doc_date>=') {
912 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
913 } elseif ($key == 't.doc_date<=') {
914 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
915 } elseif ($key == 't.doc_date>') {
916 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
917 } elseif ($key == 't.doc_date<') {
918 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
919 } elseif ($key == 't.numero_compte>=') {
920 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
921 } elseif ($key == 't.numero_compte<=') {
922 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
923 } elseif ($key == 't.subledger_account>=') {
924 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
925 } elseif ($key == 't.subledger_account<=') {
926 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
927 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
928 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
929 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
930 $sqlwhere[] = $this->db->sanitize($key).' LIKE \''.$this->db->escape($this->db->escapeforlike($value)).'%\'';
931 } elseif ($key == 't.date_creation>=') {
932 $sqlwhere[] = 't.date_creation >= \''.$this->db->idate($value).'\'';
933 } elseif ($key == 't.date_creation<=') {
934 $sqlwhere[] = 't.date_creation <= \''.$this->db->idate($value).'\'';
935 } elseif ($key == 't.date_export>=') {
936 $sqlwhere[] = 't.date_export >= \''.$this->db->idate($value).'\'';
937 } elseif ($key == 't.date_export<=') {
938 $sqlwhere[] = 't.date_export <= \''.$this->db->idate($value).'\'';
939 } elseif ($key == 't.date_validated>=') {
940 $sqlwhere[] = 't.date_validated >= \''.$this->db->idate($value).'\'';
941 } elseif ($key == 't.date_validated<=') {
942 $sqlwhere[] = 't.date_validated <= \''.$this->db->idate($value).'\'';
943 } elseif ($key == 't.date_lim_reglement>=') {
944 $sqlwhere[] = 't.date_lim_reglement>=\''.$this->db->idate($value).'\'';
945 } elseif ($key == 't.date_lim_reglement<=') {
946 $sqlwhere[] = 't.date_lim_reglement<=\''.$this->db->idate($value).'\'';
947 } elseif ($key == 't.credit' || $key == 't.debit') {
948 $sqlwhere[] = natural_search($key, $value, 1, 1);
949 } elseif ($key == 't.reconciled_option') {
950 $sqlwhere[] = 't.lettering_code IS NULL';
951 } elseif ($key == 't.code_journal' && !empty($value)) {
952 if (is_array($value)) {
953 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
954 } else {
955 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
956 }
957 } elseif ($key == 't.search_accounting_code_in' && !empty($value)) {
958 $sqlwhere[] = 't.numero_compte IN ('.$this->db->sanitize($value, 1).')';
959 } else {
960 $sqlwhere[] = natural_search($key, $value, 0, 1);
961 }
962 }
963 }
964 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
965 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
966 if (count($sqlwhere) > 0) {
967 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
968 }
969 // Filter by ledger account or subledger account
970 if (!empty($option)) {
971 $sql .= " AND t.subledger_account IS NOT NULL";
972 $sql .= " AND t.subledger_account <> ''";
973 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
974 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
975 } else {
976 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
977 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
978 }
979
980 if (!$countonly) {
981 $sql .= $this->db->order($sortfield, $sortorder);
982 if (!empty($limit)) {
983 $sql .= $this->db->plimit($limit + 1, $offset);
984 }
985 }
986
987 $resql = $this->db->query($sql);
988 if ($resql) {
989 if ($countonly) {
990 $obj = $this->db->fetch_object($resql);
991 if ($obj) {
992 $num = $obj->nb;
993 }
994 } else {
995 $num = $this->db->num_rows($resql);
996
997 $i = 0;
998 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
999 $line = new BookKeepingLine($this->db);
1000
1001 $line->id = $obj->rowid;
1002
1003 $line->doc_date = $this->db->jdate($obj->doc_date);
1004 $line->doc_type = $obj->doc_type;
1005 $line->doc_ref = $obj->doc_ref;
1006 $line->fk_doc = $obj->fk_doc;
1007 $line->fk_docdet = $obj->fk_docdet;
1008 $line->thirdparty_code = $obj->thirdparty_code;
1009 $line->subledger_account = $obj->subledger_account;
1010 $line->subledger_label = $obj->subledger_label;
1011 $line->numero_compte = $obj->numero_compte;
1012 $line->label_compte = $obj->label_compte;
1013 $line->label_operation = $obj->label_operation;
1014 $line->debit = $obj->debit;
1015 $line->credit = $obj->credit;
1016 $line->montant = $obj->amount; // deprecated
1017 $line->amount = $obj->amount;
1018 $line->sens = $obj->sens;
1019 $line->multicurrency_amount = $obj->multicurrency_amount;
1020 $line->multicurrency_code = $obj->multicurrency_code;
1021 $line->lettering_code = $obj->lettering_code;
1022 $line->date_lettering = $this->db->jdate($obj->date_lettering);
1023 $line->fk_user_author = $obj->fk_user_author;
1024 $line->import_key = $obj->import_key;
1025 $line->code_journal = $obj->code_journal;
1026 $line->journal_label = $obj->journal_label;
1027 $line->piece_num = $obj->piece_num;
1028 $line->date_creation = $this->db->jdate($obj->date_creation);
1029 $line->date_export = $this->db->jdate($obj->date_export);
1030 $line->date_validation = $this->db->jdate($obj->date_validation);
1031 // Due date
1032 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1033 $line->import_key = $obj->import_key;
1034
1035 $this->lines[] = $line;
1036
1037 $i++;
1038 }
1039 }
1040 $this->db->free($resql);
1041
1042 return $num;
1043 } else {
1044 $this->errors[] = 'Error '.$this->db->lasterror();
1045 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1046
1047 return -1;
1048 }
1049 }
1050
1063 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $showAlreadyExportMovements = 1)
1064 {
1065 global $conf;
1066
1067 dol_syslog(__METHOD__, LOG_DEBUG);
1068
1069 $sql = 'SELECT';
1070 $sql .= ' t.rowid,';
1071 $sql .= " t.doc_date,";
1072 $sql .= " t.doc_type,";
1073 $sql .= " t.doc_ref,";
1074 $sql .= " t.fk_doc,";
1075 $sql .= " t.fk_docdet,";
1076 $sql .= " t.thirdparty_code,";
1077 $sql .= " t.subledger_account,";
1078 $sql .= " t.subledger_label,";
1079 $sql .= " t.numero_compte,";
1080 $sql .= " t.label_compte,";
1081 $sql .= " t.label_operation,";
1082 $sql .= " t.debit,";
1083 $sql .= " t.credit,";
1084 $sql .= " t.lettering_code,";
1085 $sql .= " t.date_lettering,";
1086 $sql .= " t.montant as amount,";
1087 $sql .= " t.sens,";
1088 $sql .= " t.fk_user_author,";
1089 $sql .= " t.import_key,";
1090 $sql .= " t.code_journal,";
1091 $sql .= " t.journal_label,";
1092 $sql .= " t.piece_num,";
1093 $sql .= " t.date_creation,";
1094 $sql .= " t.date_lim_reglement,";
1095 $sql .= " t.tms as date_modification,";
1096 $sql .= " t.date_export,";
1097 $sql .= " t.date_validated as date_validation";
1098 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1099 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1100 if ($showAlreadyExportMovements == 0) {
1101 $sql .= " AND t.date_export IS NULL";
1102 }
1103
1104 // Manage filter
1105 if (is_array($filter)) { // deprecated, use $filter = USF syntax
1106 dol_syslog("You are using a deprecated use of fetchAll. filter parameter mus be an USF string now.", LOG_WARNING);
1107 $sqlwhere = array();
1108 if (count($filter) > 0) {
1109 foreach ($filter as $key => $value) {
1110 if ($key == 't.doc_date') {
1111 $sqlwhere[] = $this->db->sanitize($key).' = \''.$this->db->idate($value).'\'';
1112 } elseif ($key == 't.doc_date>=') {
1113 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
1114 } elseif ($key == 't.doc_date<=') {
1115 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
1116 } elseif ($key == 't.doc_date>') {
1117 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
1118 } elseif ($key == 't.doc_date<') {
1119 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
1120 } elseif ($key == 't.numero_compte>=') {
1121 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1122 } elseif ($key == 't.numero_compte<=') {
1123 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1124 } elseif ($key == 't.subledger_account>=') {
1125 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1126 } elseif ($key == 't.subledger_account<=') {
1127 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1128 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1129 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
1130 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1131 $sqlwhere[] = $this->db->sanitize($key).' LIKE \''.$this->db->escape($value).'%\'';
1132 } elseif ($key == 't.date_creation>=') {
1133 $sqlwhere[] = 't.date_creation >= \''.$this->db->idate($value).'\'';
1134 } elseif ($key == 't.date_creation<=') {
1135 $sqlwhere[] = 't.date_creation <= \''.$this->db->idate($value).'\'';
1136 } elseif ($key == 't.tms>=') {
1137 $sqlwhere[] = 't.tms >= \''.$this->db->idate($value).'\'';
1138 } elseif ($key == 't.tms<=') {
1139 $sqlwhere[] = 't.tms <= \''.$this->db->idate($value).'\'';
1140 } elseif ($key == 't.date_export>=') {
1141 $sqlwhere[] = 't.date_export >= \''.$this->db->idate($value).'\'';
1142 } elseif ($key == 't.date_export<=') {
1143 $sqlwhere[] = 't.date_export <= \''.$this->db->idate($value).'\'';
1144 } elseif ($key == 't.date_validated>=') {
1145 $sqlwhere[] = 't.date_validated >= \''.$this->db->idate($value).'\'';
1146 } elseif ($key == 't.date_validated<=') {
1147 $sqlwhere[] = 't.date_validated <= \''.$this->db->idate($value).'\'';
1148 } elseif ($key == 't.credit' || $key == 't.debit') {
1149 $sqlwhere[] = natural_search($key, $value, 1, 1);
1150 } elseif ($key == 't.code_journal' && !empty($value)) {
1151 if (is_array($value)) {
1152 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1153 } else {
1154 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1155 }
1156 } else {
1157 $sqlwhere[] = natural_search($key, $value, 0, 1);
1158 }
1159 }
1160 }
1161 if (count($sqlwhere) > 0) {
1162 $sql .= ' AND '.implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1163 }
1164
1165 $filter = '';
1166 }
1167
1168 // Manage filter
1169 $errormessage = '';
1170 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1171 if ($errormessage) {
1172 $this->errors[] = $errormessage;
1173 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1174 return -1;
1175 }
1176
1177 if (!empty($sortfield)) {
1178 $sql .= $this->db->order($sortfield, $sortorder);
1179 }
1180 if (!empty($limit)) {
1181 $sql .= $this->db->plimit($limit + 1, $offset);
1182 }
1183 $this->lines = array();
1184
1185 $resql = $this->db->query($sql);
1186 if ($resql) {
1187 $num = $this->db->num_rows($resql);
1188
1189 $i = 0;
1190 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1191 $line = new BookKeepingLine($this->db);
1192
1193 $line->id = $obj->rowid;
1194
1195 $line->doc_date = $this->db->jdate($obj->doc_date);
1196 $line->doc_type = $obj->doc_type;
1197 $line->doc_ref = $obj->doc_ref;
1198 $line->fk_doc = $obj->fk_doc;
1199 $line->fk_docdet = $obj->fk_docdet;
1200 $line->thirdparty_code = $obj->thirdparty_code;
1201 $line->subledger_account = $obj->subledger_account;
1202 $line->subledger_label = $obj->subledger_label;
1203 $line->numero_compte = $obj->numero_compte;
1204 $line->label_compte = $obj->label_compte;
1205 $line->label_operation = $obj->label_operation;
1206 $line->debit = $obj->debit;
1207 $line->credit = $obj->credit;
1208 $line->montant = $obj->amount; // deprecated
1209 $line->amount = $obj->amount;
1210 $line->sens = $obj->sens;
1211 $line->lettering_code = $obj->lettering_code;
1212 $line->date_lettering = $this->db->jdate($obj->date_lettering);
1213 $line->fk_user_author = $obj->fk_user_author;
1214 $line->import_key = $obj->import_key;
1215 $line->code_journal = $obj->code_journal;
1216 $line->journal_label = $obj->journal_label;
1217 $line->piece_num = $obj->piece_num;
1218 $line->date_creation = $this->db->jdate($obj->date_creation);
1219 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1220 $line->date_modification = $this->db->jdate($obj->date_modification);
1221 $line->date_export = $this->db->jdate($obj->date_export);
1222 $line->date_validation = $this->db->jdate($obj->date_validation);
1223
1224 $this->lines[] = $line;
1225
1226 $i++;
1227 }
1228 $this->db->free($resql);
1229
1230 return $num;
1231 } else {
1232 $this->errors[] = 'Error '.$this->db->lasterror();
1233 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1234 return -1;
1235 }
1236 }
1237
1250 public function fetchAllBalance($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $option = 0)
1251 {
1252 global $conf;
1253
1254 $this->lines = array();
1255
1256 dol_syslog(__METHOD__, LOG_DEBUG);
1257
1258 $sql = 'SELECT';
1259 $sql .= " t.numero_compte,";
1260 if (!empty($option)) {
1261 $sql .= " t.subledger_account,";
1262 $sql .= " t.subledger_label,";
1263 }
1264 $sql .= " SUM(t.debit) as debit,";
1265 $sql .= " SUM(t.credit) as credit";
1266 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1267 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1268
1269 // Manage filter
1270 if (is_array($filter)) {
1271 $sqlwhere = array();
1272 if (count($filter) > 0) {
1273 foreach ($filter as $key => $value) {
1274 if ($key == 't.doc_date') {
1275 $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate($value)."'";
1276 } elseif ($key == 't.doc_date>=') {
1277 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
1278 } elseif ($key == 't.doc_date<=') {
1279 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
1280 } elseif ($key == 't.doc_date>') {
1281 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
1282 } elseif ($key == 't.doc_date<') {
1283 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
1284 } elseif ($key == 't.numero_compte>=') {
1285 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1286 } elseif ($key == 't.numero_compte<=') {
1287 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1288 } elseif ($key == 't.subledger_account>=') {
1289 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1290 } elseif ($key == 't.subledger_account<=') {
1291 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1292 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1293 $sqlwhere[] = $this->db->sanitize($key)." = ".((int) $value);
1294 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1295 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1296 } elseif ($key == 't.subledger_label') {
1297 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1298 } elseif ($key == 't.code_journal' && !empty($value)) {
1299 if (is_array($value)) {
1300 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1301 } else {
1302 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1303 }
1304 } elseif ($key == 't.reconciled_option') {
1305 $sqlwhere[] = 't.lettering_code IS NULL';
1306 } else {
1307 $sqlwhere[] = $this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
1308 }
1309 }
1310 }
1311 if (count($sqlwhere) > 0) {
1312 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1313 }
1314
1315 $filter = '';
1316 }
1317
1318 // Manage filter
1319 $errormessage = '';
1320 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1321 if ($errormessage) {
1322 $this->errors[] = $errormessage;
1323 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1324 return -1;
1325 }
1326
1327 if (!empty($option)) {
1328 $sql .= " AND t.subledger_account IS NOT NULL";
1329 $sql .= " AND t.subledger_account <> ''";
1330 $sql .= " GROUP BY t.numero_compte, t.subledger_account, t.subledger_label";
1331 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
1332 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
1333 } else {
1334 $sql .= ' GROUP BY t.numero_compte';
1335 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
1336 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
1337 }
1338
1339 if (!empty($sortfield)) {
1340 $sql .= $this->db->order($sortfield, $sortorder);
1341 }
1342 if (!empty($limit)) {
1343 $sql .= $this->db->plimit($limit + 1, $offset);
1344 }
1345
1346 //print $sql;
1347 $resql = $this->db->query($sql);
1348
1349 if ($resql) {
1350 $num = $this->db->num_rows($resql);
1351
1352 $i = 0;
1353 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1354 $line = new BookKeepingLine($this->db);
1355
1356 $line->numero_compte = $obj->numero_compte;
1357 //$line->label_compte = $obj->label_compte;
1358 if (!empty($option)) {
1359 $line->subledger_account = $obj->subledger_account;
1360 $line->subledger_label = $obj->subledger_label;
1361 }
1362 $line->debit = $obj->debit;
1363 $line->credit = $obj->credit;
1364
1365 $this->lines[] = $line;
1366
1367 $i++;
1368 }
1369 $this->db->free($resql);
1370
1371 return $num;
1372 } else {
1373 $this->errors[] = 'Error '.$this->db->lasterror();
1374 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1375
1376 return -1;
1377 }
1378 }
1379
1388 public function update(User $user, $notrigger = 0, $mode = '')
1389 {
1390 global $langs;
1391 $error = 0;
1392
1393 dol_syslog(__METHOD__, LOG_DEBUG);
1394
1395 // Clean parameters
1396 if (isset($this->doc_type)) {
1397 $this->doc_type = trim($this->doc_type);
1398 }
1399 if (isset($this->doc_ref)) {
1400 $this->doc_ref = trim($this->doc_ref);
1401 }
1402 if (isset($this->fk_doc)) {
1403 $this->fk_doc = (int) $this->fk_doc;
1404 }
1405 if (isset($this->fk_docdet)) {
1406 $this->fk_docdet = (int) $this->fk_docdet;
1407 }
1408 if (isset($this->thirdparty_code)) {
1409 $this->thirdparty_code = trim($this->thirdparty_code);
1410 }
1411 if (isset($this->subledger_account)) {
1412 $this->subledger_account = trim($this->subledger_account);
1413 }
1414 if (isset($this->subledger_label)) {
1415 $this->subledger_label = trim($this->subledger_label);
1416 }
1417 if (isset($this->numero_compte)) {
1418 $this->numero_compte = trim($this->numero_compte);
1419 }
1420 if (isset($this->label_compte)) {
1421 $this->label_compte = trim($this->label_compte);
1422 }
1423 if (isset($this->label_operation)) {
1424 $this->label_operation = trim($this->label_operation);
1425 }
1426 if (isset($this->sens)) {
1427 $this->sens = trim($this->sens);
1428 }
1429 if (isset($this->import_key)) {
1430 $this->import_key = trim($this->import_key);
1431 }
1432 if (isset($this->code_journal)) {
1433 $this->code_journal = trim($this->code_journal);
1434 }
1435 if (isset($this->journal_label)) {
1436 $this->journal_label = trim($this->journal_label);
1437 }
1438 if (isset($this->piece_num)) {
1439 $this->piece_num = (int) $this->piece_num;
1440 }
1441
1442 $result = $this->canModifyBookkeeping($this->id, $mode);
1443 if ($result < 0) {
1444 return -1;
1445 } elseif ($result == 0) {
1446 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1447 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1448 } else {
1449 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1450 }
1451 return -1;
1452 }
1453
1454 $this->debit = (float) price2num($this->debit, 'MT');
1455 $this->credit = (float) price2num($this->credit, 'MT');
1456 $this->montant = (float) price2num($this->montant, 'MT');
1457
1458 // Check parameters
1459 // Put here code to add a control on parameters values
1460
1461 // Update request
1462 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.$mode.' SET';
1463 $sql .= ' doc_date = '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'null').',';
1464 $sql .= ' doc_type = '.(isset($this->doc_type) ? "'".$this->db->escape($this->doc_type)."'" : "null").',';
1465 $sql .= ' doc_ref = '.(isset($this->doc_ref) ? "'".$this->db->escape($this->doc_ref)."'" : "null").',';
1466 $sql .= ' fk_doc = '.(isset($this->fk_doc) ? $this->fk_doc : "null").',';
1467 $sql .= ' fk_docdet = '.(isset($this->fk_docdet) ? $this->fk_docdet : "null").',';
1468 $sql .= ' thirdparty_code = '.(isset($this->thirdparty_code) ? "'".$this->db->escape($this->thirdparty_code)."'" : "null").',';
1469 $sql .= ' subledger_account = '.(isset($this->subledger_account) ? "'".$this->db->escape($this->subledger_account)."'" : "null").',';
1470 $sql .= ' subledger_label = '.(isset($this->subledger_label) ? "'".$this->db->escape($this->subledger_label)."'" : "null").',';
1471 $sql .= ' numero_compte = '.(isset($this->numero_compte) ? "'".$this->db->escape($this->numero_compte)."'" : "null").',';
1472 $sql .= ' label_compte = '.(isset($this->label_compte) ? "'".$this->db->escape($this->label_compte)."'" : "null").',';
1473 $sql .= ' label_operation = '.(isset($this->label_operation) ? "'".$this->db->escape($this->label_operation)."'" : "null").',';
1474 $sql .= ' debit = '.(isset($this->debit) ? $this->debit : "null").',';
1475 $sql .= ' credit = '.(isset($this->credit) ? $this->credit : "null").',';
1476 $sql .= ' montant = '.(isset($this->montant) ? $this->montant : "null").',';
1477 $sql .= ' sens = '.(isset($this->sens) ? "'".$this->db->escape($this->sens)."'" : "null").',';
1478 $sql .= ' fk_user_author = '.(isset($this->fk_user_author) ? $this->fk_user_author : "null").',';
1479 $sql .= ' import_key = '.(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").',';
1480 $sql .= ' code_journal = '.(isset($this->code_journal) ? "'".$this->db->escape($this->code_journal)."'" : "null").',';
1481 $sql .= ' journal_label = '.(isset($this->journal_label) ? "'".$this->db->escape($this->journal_label)."'" : "null").',';
1482 $sql .= ' piece_num = '.(isset($this->piece_num) ? $this->piece_num : "null");
1483 $sql .= ' WHERE rowid='.((int) $this->id);
1484
1485 $this->db->begin();
1486
1487 $resql = $this->db->query($sql);
1488 if (!$resql) {
1489 $error++;
1490 $this->errors[] = 'Error '.$this->db->lasterror();
1491 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1492 }
1493
1494 // Call triggers
1495 if (! $error && ! $notrigger) {
1496 $result = $this->call_trigger('BOOKKEEPING_MODIFY', $user);
1497 if ($result < 0) {
1498 $error++;
1499 }
1500 }
1501
1502 // Commit or rollback
1503 if ($error) {
1504 $this->db->rollback();
1505
1506 return -1 * $error;
1507 } else {
1508 $this->db->commit();
1509
1510 return 1;
1511 }
1512 }
1513
1523 public function updateByMvt($piece_num = '', $field = '', $value = '', $mode = '')
1524 {
1525 global $conf;
1526 $error = 0;
1527
1528 $sql_filter = $this->getCanModifyBookkeepingSQL();
1529 if (!isset($sql_filter)) {
1530 return -1;
1531 }
1532
1533 $this->db->begin();
1534
1535 $sql = "UPDATE ".$this->db->prefix().$this->table_element.$mode;
1536 $sql .= " SET ".$this->db->sanitize($field)." = ".(is_numeric($value) ? ((float) $value) : "'".$this->db->escape($value)."'");
1537 $sql .= " WHERE piece_num = ".((int) $piece_num);
1538 $sql .= " AND entity = " . ((int) $conf->entity);
1539 $sql .= $sql_filter;
1540
1541 $resql = $this->db->query($sql);
1542
1543 if (!$resql) {
1544 $error++;
1545 $this->errors[] = 'Error '.$this->db->lasterror();
1546 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1547 }
1548 if ($error) {
1549 $this->db->rollback();
1550
1551 return -1 * $error;
1552 } else {
1553 $this->db->commit();
1554
1555 return 1;
1556 }
1557 }
1558
1567 public function delete(User $user, $notrigger = 0, $mode = '')
1568 {
1569 global $langs;
1570
1571 dol_syslog(__METHOD__, LOG_DEBUG);
1572
1573 $result = $this->canModifyBookkeeping($this->id, $mode);
1574 if ($result < 0) {
1575 return -1;
1576 } elseif ($result == 0) {
1577 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1578 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1579 } else {
1580 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1581 }
1582 return -1;
1583 }
1584
1585 $error = 0;
1586
1587 $this->db->begin();
1588
1589 // Call triggers
1590 if (! $error && ! $notrigger) {
1591 $result = $this->call_trigger('BOOKKEEPING_DELETE', $user);
1592 if ($result < 0) {
1593 $error++;
1594 }
1595 }
1596
1597 if (!$error) {
1598 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.$mode;
1599 $sql .= ' WHERE rowid='.((int) $this->id);
1600
1601 $resql = $this->db->query($sql);
1602 if (!$resql) {
1603 $error++;
1604 $this->errors[] = 'Error '.$this->db->lasterror();
1605 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1606 }
1607 }
1608
1609 // Commit or rollback
1610 if ($error) {
1611 $this->db->rollback();
1612
1613 return -1 * $error;
1614 } else {
1615 $this->db->commit();
1616
1617 return 1;
1618 }
1619 }
1620
1628 public function deleteByImportkey($importkey, $mode = '')
1629 {
1630 $this->db->begin();
1631
1632 $sql_filter = $this->getCanModifyBookkeepingSQL();
1633 if (!isset($sql_filter)) {
1634 return -1;
1635 }
1636
1637 // first check if line not yet in bookkeeping
1638 $sql = "DELETE";
1639 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1640 $sql .= " WHERE import_key = '".$this->db->escape($importkey)."'";
1641 $sql .= $sql_filter;
1642
1643 $resql = $this->db->query($sql);
1644
1645 if (!$resql) {
1646 $this->errors[] = "Error ".$this->db->lasterror();
1647 dol_syslog(get_class($this)."::delete Error ".$this->db->lasterror(), LOG_ERR);
1648 $this->db->rollback();
1649 return -1;
1650 }
1651
1652 $this->db->commit();
1653 return 1;
1654 }
1655
1665 public function deleteByYearAndJournal($delyear = 0, $journal = '', $mode = '', $delmonth = 0)
1666 {
1667 global $conf, $langs;
1668
1669 if (empty($delyear) && empty($journal)) {
1670 $this->error = 'ErrorOneFieldRequired';
1671 return -1;
1672 }
1673 if (!empty($delmonth) && empty($delyear)) {
1674 $this->error = 'YearRequiredIfMonthDefined';
1675 return -2;
1676 }
1677
1678 $sql_filter = $this->getCanModifyBookkeepingSQL();
1679 if (!isset($sql_filter)) {
1680 return -1;
1681 }
1682
1683 $this->db->begin();
1684
1685 // Delete record in bookkeeping
1686 $sql = "DELETE";
1687 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1688 $sql .= " WHERE 1 = 1";
1689 $sql .= dolSqlDateFilter('doc_date', 0, $delmonth, $delyear);
1690 if (!empty($journal)) {
1691 $sql .= " AND code_journal = '".$this->db->escape($journal)."'";
1692 }
1693 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1694 // Exclusion of validated entries at the time of deletion
1695 $sql .= " AND date_validated IS NULL";
1696 $sql .= $sql_filter;
1697
1698 // TODO: In a future we must forbid deletion if record is inside a closed fiscal period.
1699
1700 $resql = $this->db->query($sql);
1701
1702 if (!$resql) {
1703 $this->errors[] = "Error ".$this->db->lasterror();
1704 foreach ($this->errors as $errmsg) {
1705 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1706 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1707 }
1708 $this->db->rollback();
1709 return -1;
1710 }
1711
1712 $this->db->commit();
1713 return 1;
1714 }
1715
1723 public function deleteMvtNum($piecenum, $mode = '')
1724 {
1725 global $conf;
1726
1727 $sql_filter = $this->getCanModifyBookkeepingSQL();
1728 if (!isset($sql_filter)) {
1729 return -1;
1730 }
1731
1732 $nbprocessed = 0;
1733
1734 $this->db->begin();
1735
1736 // first check if line not yet in bookkeeping
1737 $sql = "DELETE";
1738 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1739 $sql .= " WHERE piece_num = ".(int) $piecenum;
1740 $sql .= " AND date_validated IS NULL"; // For security, exclusion of validated entries at the time of deletion
1741 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1742 $sql .= $sql_filter;
1743
1744 $resql = $this->db->query($sql);
1745
1746 if (!$resql) {
1747 $this->errors[] = "Error ".$this->db->lasterror();
1748 foreach ($this->errors as $errmsg) {
1749 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1750 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1751 }
1752 $this->db->rollback();
1753 return -1;
1754 } else {
1755 $nbprocessed = $this->db->affected_rows($resql);
1756 }
1757
1758 $this->db->commit();
1759
1760 return $nbprocessed;
1761 }
1762
1770 public function createFromClone(User $user, $fromid)
1771 {
1772 dol_syslog(__METHOD__, LOG_DEBUG);
1773
1774 $error = 0;
1775 $object = new BookKeeping($this->db);
1776
1777 $this->db->begin();
1778
1779 // Load source object
1780 $object->fetch($fromid);
1781 // Reset object
1782 $object->id = 0;
1783
1784 // Clear fields
1785 // ...
1786
1787 // Create clone
1788 $object->context['createfromclone'] = 'createfromclone';
1789 $result = $object->create($user);
1790
1791 // Other options
1792 if ($result < 0) {
1793 $error++;
1794 $this->errors = $object->errors;
1795 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1796 }
1797
1798 unset($object->context['createfromclone']);
1799
1800 // End
1801 if (!$error) {
1802 $this->db->commit();
1803
1804 return $object->id;
1805 } else {
1806 $this->db->rollback();
1807
1808 return -1;
1809 }
1810 }
1811
1818 public function initAsSpecimen()
1819 {
1820 global $user;
1821
1822 $now = dol_now();
1823
1824 $this->id = 0;
1825 $this->doc_date = $now;
1826 $this->doc_type = '';
1827 $this->doc_ref = '';
1828 $this->fk_doc = 0;
1829 $this->fk_docdet = 0;
1830 $this->thirdparty_code = 'CU001';
1831 $this->subledger_account = '41100001';
1832 $this->subledger_label = 'My customer company';
1833 $this->numero_compte = '411';
1834 $this->label_compte = 'Customer';
1835 $this->label_operation = 'Sales of pea';
1836 $this->debit = 99.9;
1837 $this->credit = 0.0;
1838 $this->amount = 0.0;
1839 $this->sens = 'D';
1840 $this->fk_user_author = $user->id;
1841 $this->import_key = '20201027';
1842 $this->code_journal = 'VT';
1843 $this->journal_label = 'Journal de vente';
1844 $this->piece_num = 1234;
1845 $this->date_creation = $now;
1846
1847 return 1;
1848 }
1849
1857 public function fetchPerMvt($piecenum, $mode = '')
1858 {
1859 global $conf;
1860
1861 $sql = "SELECT piece_num, doc_date, code_journal, journal_label, doc_ref, doc_type,";
1862 $sql .= " date_creation, tms as date_modification, date_validated as date_validation, date_lim_reglement, import_key";
1863 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1864 if ($mode != "_tmp") {
1865 $sql .= ", date_export";
1866 }
1867 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1868 $sql .= " WHERE piece_num = ".((int) $piecenum);
1869 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1870
1871 dol_syslog(__METHOD__, LOG_DEBUG);
1872 $result = $this->db->query($sql);
1873 if ($result) {
1874 $obj = $this->db->fetch_object($result);
1875
1876 $this->piece_num = $obj->piece_num;
1877 $this->code_journal = $obj->code_journal;
1878 $this->journal_label = $obj->journal_label;
1879 $this->doc_date = $this->db->jdate($obj->doc_date);
1880 $this->doc_ref = $obj->doc_ref;
1881 $this->doc_type = $obj->doc_type;
1882 $this->date_creation = $this->db->jdate($obj->date_creation);
1883 $this->date_modification = $this->db->jdate($obj->date_modification);
1884 if ($mode != "_tmp") {
1885 $this->date_export = $this->db->jdate($obj->date_export);
1886 }
1887 $this->date_validation = $this->db->jdate($obj->date_validation);
1888 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1889 $this->import_key = $obj->import_key;
1890 } else {
1891 $this->error = "Error ".$this->db->lasterror();
1892 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1893 return -1;
1894 }
1895
1896 return 1;
1897 }
1898
1905 public function getNextNumMvt($mode = '')
1906 {
1907 global $conf;
1908
1909 $sql = "SELECT MAX(piece_num)+1 as max FROM ".$this->db->prefix().$this->table_element.$mode;
1910 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1911
1912 dol_syslog(get_class($this)."::getNextNumMvt", LOG_DEBUG);
1913
1914 $result = $this->db->query($sql);
1915
1916 if ($result) {
1917 $obj = $this->db->fetch_object($result);
1918 if ($obj) {
1919 $result = $obj->max;
1920 }
1921 if (empty($result)) {
1922 $result = 1;
1923 }
1924 return $result;
1925 } else {
1926 $this->error = "Error ".$this->db->lasterror();
1927 dol_syslog(get_class($this)."::getNextNumMvt ".$this->error, LOG_ERR);
1928 return -1;
1929 }
1930 }
1931
1939 public function fetchAllPerMvt($piecenum, $mode = '')
1940 {
1941 global $conf;
1942
1943 $sql = "SELECT rowid, doc_date, doc_type,";
1944 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1945 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1946 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
1947 $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1948 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1949 if ($mode != "_tmp") {
1950 $sql .= ", date_export";
1951 }
1952 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1953 $sql .= " WHERE piece_num = ".((int) $piecenum);
1954 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1955
1956 dol_syslog(__METHOD__, LOG_DEBUG);
1957 $result = $this->db->query($sql);
1958 if ($result) {
1959 while ($obj = $this->db->fetch_object($result)) {
1960 $line = new BookKeepingLine($this->db);
1961
1962 $line->id = $obj->rowid;
1963
1964 $line->doc_date = $this->db->jdate($obj->doc_date);
1965 $line->doc_type = $obj->doc_type;
1966 $line->doc_ref = $obj->doc_ref;
1967 $line->fk_doc = $obj->fk_doc;
1968 $line->fk_docdet = $obj->fk_docdet;
1969 $line->thirdparty_code = $obj->thirdparty_code;
1970 $line->subledger_account = $obj->subledger_account;
1971 $line->subledger_label = $obj->subledger_label;
1972 $line->numero_compte = $obj->numero_compte;
1973 $line->label_compte = $obj->label_compte;
1974 $line->label_operation = $obj->label_operation;
1975 $line->debit = $obj->debit;
1976 $line->credit = $obj->credit;
1977 $line->montant = $obj->amount;
1978 $line->amount = $obj->amount;
1979 $line->sens = $obj->sens;
1980 $line->code_journal = $obj->code_journal;
1981 $line->journal_label = $obj->journal_label;
1982 $line->piece_num = $obj->piece_num;
1983 $line->date_creation = $obj->date_creation;
1984 $line->date_modification = $obj->date_modification;
1985 if ($mode != "_tmp") {
1986 $line->date_export = $obj->date_export;
1987 }
1988 $line->date_validation = $obj->date_validation;
1989
1990 $this->linesmvt[] = $line;
1991 }
1992 } else {
1993 $this->error = "Error ".$this->db->lasterror();
1994 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1995 return -1;
1996 }
1997
1998 return 1;
1999 }
2000
2001 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2008 public function export_bookkeeping($model = 'ebp')
2009 {
2010 // phpcs:enable
2011 global $conf;
2012
2013 $sql = "SELECT rowid, doc_date, doc_type,";
2014 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
2015 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
2016 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, piece_num,";
2017 $sql .= " date_validated as date_validation";
2018 $sql .= " FROM ".$this->db->prefix().$this->table_element;
2019 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2020
2021 dol_syslog(get_class($this)."::export_bookkeeping", LOG_DEBUG);
2022
2023 $resql = $this->db->query($sql);
2024
2025 if ($resql) {
2026 $this->linesexport = array();
2027
2028 $num = $this->db->num_rows($resql);
2029 while ($obj = $this->db->fetch_object($resql)) {
2030 $line = new BookKeepingLine($this->db);
2031
2032 $line->id = $obj->rowid;
2033
2034 $line->doc_date = $this->db->jdate($obj->doc_date);
2035 $line->doc_type = $obj->doc_type;
2036 $line->doc_ref = $obj->doc_ref;
2037 $line->fk_doc = $obj->fk_doc;
2038 $line->fk_docdet = $obj->fk_docdet;
2039 $line->thirdparty_code = $obj->thirdparty_code;
2040 $line->subledger_account = $obj->subledger_account;
2041 $line->subledger_label = $obj->subledger_label;
2042 $line->numero_compte = $obj->numero_compte;
2043 $line->label_compte = $obj->label_compte;
2044 $line->label_operation = $obj->label_operation;
2045 $line->debit = $obj->debit;
2046 $line->credit = $obj->credit;
2047 $line->montant = $obj->amount;
2048 $line->amount = $obj->amount;
2049 $line->sens = $obj->sens;
2050 $line->code_journal = $obj->code_journal;
2051 $line->piece_num = $obj->piece_num;
2052 $line->date_validation = $obj->date_validation;
2053
2054 $this->linesexport[] = $line;
2055 }
2056 $this->db->free($resql);
2057
2058 return $num;
2059 } else {
2060 $this->error = "Error ".$this->db->lasterror();
2061 dol_syslog(get_class($this)."::export_bookkeeping ".$this->error, LOG_ERR);
2062 return -1;
2063 }
2064 }
2065
2073 public function transformTransaction($direction = 0, $piece_num = '')
2074 {
2075 global $conf;
2076
2077 $error = 0;
2078
2079 $sql_filter = $this->getCanModifyBookkeepingSQL();
2080
2081 if (!isset($sql_filter)) {
2082 return -1;
2083 }
2084
2085 $this->db->begin();
2086
2087 if ($direction == 0) {
2088 $next_piecenum = $this->getNextNumMvt();
2089 $now = dol_now();
2090
2091 if ($next_piecenum < 0) {
2092 $error++;
2093 }
2094
2095 if (!$error) {
2096 // Delete if there is an empty line
2097 $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";
2098 $resql = $this->db->query($sql);
2099 if (!$resql) {
2100 $error++;
2101 $this->errors[] = 'Error '.$this->db->lasterror();
2102 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2103 }
2104 }
2105
2106 if (!$error) {
2107 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.' (doc_date, doc_type,';
2108 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2109 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2110 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num, date_creation)';
2111 $sql .= ' SELECT doc_date, doc_type,';
2112 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2113 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2114 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, '.((int) $next_piecenum).", '".$this->db->idate($now)."'";
2115 $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);
2116 $sql .= $sql_filter;
2117 $resql = $this->db->query($sql);
2118 if (!$resql) {
2119 $error++;
2120 $this->errors[] = 'Error '.$this->db->lasterror();
2121 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2122 }
2123 }
2124
2125 if (!$error) {
2126 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2127 $resql = $this->db->query($sql);
2128 if (!$resql) {
2129 $error++;
2130 $this->errors[] = 'Error '.$this->db->lasterror();
2131 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2132 }
2133 }
2134 } elseif ($direction == 1) {
2135 if (!$error) {
2136 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2137 $resql = $this->db->query($sql);
2138 if (!$resql) {
2139 $error++;
2140 $this->errors[] = 'Error '.$this->db->lasterror();
2141 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2142 }
2143 }
2144
2145 if (!$error) {
2146 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.'_tmp (doc_date, doc_type,';
2147 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2148 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2149 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num)';
2150 $sql .= ' SELECT doc_date, doc_type,';
2151 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2152 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2153 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num';
2154 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2155 $sql .= $sql_filter;
2156 $resql = $this->db->query($sql);
2157 if (!$resql) {
2158 $error++;
2159 $this->errors[] = 'Error '.$this->db->lasterror();
2160 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2161 }
2162 }
2163
2164 if (!$error) {
2165 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2166 $sql .= $sql_filter;
2167 $resql = $this->db->query($sql);
2168 if (!$resql) {
2169 $error++;
2170 $this->errors[] = 'Error '.$this->db->lasterror();
2171 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2172 }
2173 }
2174 }
2175 if (!$error) {
2176 $this->db->commit();
2177 return 1;
2178 } else {
2179 $this->db->rollback();
2180 return -1;
2181 }
2182 /*
2183 $sql = "DELETE FROM ";
2184 $sql .= " FROM " . $this->db->prefix() . "accounting_bookkeeping as ab";
2185 $sql .= " LEFT JOIN " . $this->db->prefix() . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2186 $sql .= " AND aa.active = 1";
2187 $sql .= " INNER JOIN " . $this->db->prefix() . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2188 $sql .= " AND asy.rowid = " . ((int) $pcgver);
2189 $sql .= " AND ab.entity IN (" . getEntity('accountancy') . ")";
2190 $sql .= " ORDER BY account_number ASC";
2191 */
2192 }
2193
2194 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2207 public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $aabase = '')
2208 {
2209 // phpcs:enable
2210 global $conf;
2211
2212 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
2213
2214 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2215
2216 $sql = "SELECT DISTINCT ab.numero_compte as account_number, aa.label as label, aa.rowid as rowid, aa.fk_pcg_version";
2217 $sql .= " FROM ".$this->db->prefix().$this->table_element." as ab";
2218 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as aa ON aa.account_number = ab.numero_compte";
2219 $sql .= " AND aa.active = 1";
2220 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2221 $sql .= " AND asy.rowid = ".((int) $pcgver);
2222 $sql .= " AND ab.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2223 $sql .= " ORDER BY account_number ASC";
2224
2225 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2226 $resql = $this->db->query($sql);
2227
2228 if (!$resql) {
2229 $this->error = "Error ".$this->db->lasterror();
2230 dol_syslog(get_class($this)."::select_account ".$this->error, LOG_ERR);
2231 return "-1";
2232 }
2233
2234 $out = ajax_combobox($htmlname, $event);
2235
2236 $options = array();
2237 $selected = null;
2238
2239 while ($obj = $this->db->fetch_object($resql)) {
2240 $label = length_accountg($obj->account_number).' - '.$obj->label;
2241
2242 $select_value_in = $obj->rowid;
2243 $select_value_out = $obj->rowid;
2244
2245 if ($select_in == 1) {
2246 $select_value_in = $obj->account_number;
2247 }
2248 if ($select_out == 1) {
2249 $select_value_out = $obj->account_number;
2250 }
2251
2252 // Remember guy's we store in database llx_facturedet the rowid of accounting_account and not the account_number
2253 // Because same account_number can be share between different accounting_system and do have the same meaning
2254 if (($selectid != '') && $selectid == $select_value_in) {
2255 $selected = $select_value_out;
2256 }
2257
2258 $options[$select_value_out] = $label;
2259 }
2260
2261 $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300');
2262 $this->db->free($resql);
2263 return $out;
2264 }
2265
2273 public function getRootAccount($account = null)
2274 {
2275 global $conf;
2276 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2277
2278 $sql = "SELECT root.rowid, root.account_number, root.label as label,";
2279 $sql .= " parent.rowid as parent_rowid, parent.account_number as parent_account_number, parent.label as parent_label";
2280 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa";
2281 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2282 $sql .= " AND asy.rowid = ".((int) $pcgver);
2283 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as parent ON aa.account_parent = parent.rowid AND parent.active = 1";
2284 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as root ON parent.account_parent = root.rowid AND root.active = 1";
2285 $sql .= " WHERE aa.account_number = '".$this->db->escape($account)."'";
2286 $sql .= " AND aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2287
2288 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2289 $resql = $this->db->query($sql);
2290 if ($resql) {
2291 $obj = '';
2292 if ($this->db->num_rows($resql)) {
2293 $obj = $this->db->fetch_object($resql);
2294 }
2295
2296 $result = array('id' => $obj->rowid, 'account_number' => $obj->account_number, 'label' => $obj->label);
2297 return $result;
2298 } else {
2299 $this->error = "Error ".$this->db->lasterror();
2300 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2301
2302 return -1;
2303 }
2304 }
2305
2306 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2313 public function get_compte_desc($account = null)
2314 {
2315 // phpcs:enable
2316 global $conf;
2317
2318 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2319 $sql = "SELECT aa.account_number, aa.label, aa.rowid, aa.fk_pcg_version, cat.label as category";
2320 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa ";
2321 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2322 $sql .= " AND aa.account_number = '".$this->db->escape($account)."'";
2323 $sql .= " AND asy.rowid = ".((int) $pcgver);
2324 $sql .= " AND aa.active = 1";
2325 $sql .= " LEFT JOIN ".$this->db->prefix()."c_accounting_category as cat ON aa.fk_accounting_category = cat.rowid";
2326 $sql .= " WHERE aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2327
2328 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2329 $resql = $this->db->query($sql);
2330 if ($resql) {
2331 $obj = (object) array('label' => '');
2332 if ($this->db->num_rows($resql)) {
2333 $obj = $this->db->fetch_object($resql);
2334 }
2335 if (empty($obj->category)) {
2336 return $obj->label;
2337 } else {
2338 return $obj->label.' ('.$obj->category.')';
2339 }
2340 } else {
2341 $this->error = "Error ".$this->db->lasterror();
2342 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2343 return "-1";
2344 }
2345 }
2346
2354 public function getCanModifyBookkeepingSQL($alias = '', $force = false)
2355 {
2356 global $conf;
2357
2358 $alias = trim($alias);
2359 $alias = !empty($alias) && strpos($alias, '.') === false ? $alias . "." : $alias;
2360
2361 if (!isset(self::$can_modify_bookkeeping_sql_cached[$alias]) || $force) {
2362 $result = $this->loadFiscalPeriods($force, 'active');
2363 if ($result < 0) {
2364 return null;
2365 }
2366
2367 $sql_list = array();
2368 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2369 $i = 0;
2370 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2371 $sql_list[$i] = "(";
2372 $sql_list[$i] .= "'".$this->db->idate($fiscal_period['date_start']) . "' <= ".$this->db->sanitize($alias)."doc_date";
2373 if (!empty($fiscal_period['date_end'])) {
2374 $sql_list[$i] .= " AND ";
2375 $sql_list[$i] .= $this->db->sanitize($alias)."doc_date <= '" . $this->db->idate($fiscal_period['date_end'])."'";
2376 }
2377 $sql_list[$i] .= ")";
2378 $i++;
2379 }
2380 }
2381 $sqlsanitized = implode(' OR ', $sql_list);
2382 self::$can_modify_bookkeeping_sql_cached[$alias] = empty($sql_list) ? "" : " AND (".$sqlsanitized.")";
2383 }
2384
2385 return self::$can_modify_bookkeeping_sql_cached[$alias];
2386 }
2387
2395 public function canModifyBookkeeping($id, $mode = '')
2396 {
2397 global $conf;
2398
2399 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2400 $result = $this->loadFiscalPeriods(false, 'closed');
2401
2402 if ($result < 0) {
2403 return -1;
2404 }
2405
2406 $bookkeeping = new BookKeeping($this->db);
2407 $result = $bookkeeping->fetch($id, null, $mode);
2408 if ($result <= 0) {
2409 return $result;
2410 }
2411
2412 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2413 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2414 if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2415 return 0;
2416 }
2417 }
2418 }
2419
2420 return 1;
2421 } else {
2422 $result = $this->loadFiscalPeriods(false, 'active');
2423 if ($result < 0) {
2424 return -1;
2425 }
2426
2427 $bookkeeping = new BookKeeping($this->db);
2428 $result = $bookkeeping->fetch($id, null, $mode);
2429
2430 if ($result <= 0) {
2431 return $result;
2432 }
2433 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2434 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2435 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'])) {
2436 return 1;
2437 }
2438 }
2439 }
2440
2441 return 0;
2442 }
2443 }
2444
2457 public function accountingLabelForOperation($thirdpartyname, $reference, $labelaccount, $full = 0)
2458 {
2459 $accountingLabelOperation = '';
2460
2461 if (!getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER')) {
2462 $truncThirdpartyName = 16;
2463 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2464 if (empty($full)) {
2465 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2466 } else {
2467 $accountingLabelOperation = $thirdpartyname;
2468 }
2469 if (!empty($reference)) {
2470 $accountingLabelOperation .= ' - '. $reference;
2471 }
2472 if (!empty($labelaccount)) {
2473 $accountingLabelOperation .= ' - '. $labelaccount;
2474 }
2475 } elseif (getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 1) {
2476 $truncThirdpartyName = 32;
2477 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2478 if (empty($full)) {
2479 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2480 } else {
2481 $accountingLabelOperation = $thirdpartyname;
2482 }
2483 if (!empty($reference)) {
2484 $accountingLabelOperation .= ' - '. $reference;
2485 }
2486 } elseif (getDolGlobalInt('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 2) {
2487 $truncThirdpartyName = 64;
2488 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2489 if (empty($full)) {
2490 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2491 } else {
2492 $accountingLabelOperation = $thirdpartyname;
2493 }
2494 }
2495
2496 return $accountingLabelOperation;
2497 }
2498
2505 public function validBookkeepingDate($date)
2506 {
2507 global $conf;
2508
2509 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2510 $result = $this->loadFiscalPeriods(false, 'closed');
2511
2512 if ($result < 0) {
2513 return -1;
2514 }
2515
2516 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2517 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2518 if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2519 return 0;
2520 }
2521 }
2522 }
2523
2524 return 1;
2525 } else {
2526 $result = $this->loadFiscalPeriods(false, 'active');
2527 if ($result < 0) {
2528 return -1;
2529 }
2530
2531 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2532 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2533 if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $date && (empty($fiscal_period['date_end']) || $date <= $fiscal_period['date_end'])) {
2534 return 1;
2535 }
2536 }
2537 }
2538
2539 return 0;
2540 }
2541 }
2542
2550 public function loadFiscalPeriods($force = false, $mode = 'active')
2551 {
2552 global $conf;
2553
2554 if ($mode == 'active') {
2555 if (!isset($conf->cache['active_fiscal_period_cached']) || $force) {
2556 $sql = "SELECT date_start, date_end";
2557 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2558 $sql .= " WHERE entity = " . ((int) $conf->entity);
2559 $sql .= " AND statut = 0";
2560
2561 $resql = $this->db->query($sql);
2562 if (!$resql) {
2563 $this->errors[] = $this->db->lasterror();
2564 return -1;
2565 }
2566
2567 $list = array();
2568 while ($obj = $this->db->fetch_object($resql)) {
2569 $list[] = array(
2570 'date_start' => $this->db->jdate($obj->date_start),
2571 'date_end' => $this->db->jdate($obj->date_end),
2572 );
2573 }
2574 $conf->cache['active_fiscal_period_cached'] = $list;
2575 }
2576 }
2577 if ($mode == 'closed') {
2578 if (!isset($conf->cache['closed_fiscal_period_cached']) || $force) {
2579 $sql = "SELECT date_start, date_end";
2580 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2581 $sql .= " WHERE entity = " . ((int) $conf->entity);
2582 $sql .= " AND statut = 1";
2583
2584 $resql = $this->db->query($sql);
2585 if (!$resql) {
2586 $this->errors[] = $this->db->lasterror();
2587 return -1;
2588 }
2589
2590 $list = array();
2591 while ($obj = $this->db->fetch_object($resql)) {
2592 $list[] = array(
2593 'date_start' => $this->db->jdate($obj->date_start),
2594 'date_end' => $this->db->jdate($obj->date_end),
2595 );
2596 }
2597 $conf->cache['closed_fiscal_period_cached'] = $list;
2598 }
2599 }
2600
2601 return 1;
2602 }
2603
2609 public function getFiscalPeriods()
2610 {
2611 global $conf;
2612 $list = array();
2613
2614 $sql = "SELECT rowid, label, date_start, date_end, statut";
2615 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2616 $sql .= " WHERE entity = " . ((int) $conf->entity);
2617 $sql .= $this->db->order('date_start', 'ASC');
2618
2619 $resql = $this->db->query($sql);
2620 if (!$resql) {
2621 $this->errors[] = $this->db->lasterror();
2622 return -1;
2623 }
2624
2625 while ($obj = $this->db->fetch_object($resql)) {
2626 $list[$obj->rowid] = array(
2627 'id' => (int) $obj->rowid,
2628 'label' => $obj->label,
2629 'date_start' => $this->db->jdate($obj->date_start),
2630 'date_end' => $this->db->jdate($obj->date_end),
2631 'status' => (int) $obj->statut,
2632 );
2633 }
2634
2635 return $list;
2636 }
2637
2646 public function getCountByMonthForFiscalPeriod($date_start, $date_end)
2647 {
2648 global $conf;
2649
2650 $total = 0;
2651 $list = array();
2652
2653 $sql = "SELECT YEAR(b.doc_date) as year";
2654 for ($i = 1; $i <= 12; $i++) {
2655 $sql .= ", SUM(".$this->db->ifsql("MONTH(b.doc_date) = ".((int) $i), "1", "0") . ") AS month".((int) $i);
2656 }
2657 $sql .= ", COUNT(b.rowid) as total";
2658 $sql .= " FROM " . $this->db->prefix() . $this->table_element . " as b";
2659 $sql .= " WHERE b.doc_date >= '" . $this->db->idate($date_start) . "'";
2660 $sql .= " AND b.doc_date <= '" . $this->db->idate($date_end) . "'";
2661 $sql .= " AND b.entity IN (" . getEntity('bookkeeping', 0) . ")"; // We don't share object for accountancy
2662
2663 // Get count for each month into the fiscal period
2664 if (getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")) {
2665 // Loop on each closed period
2666 $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)";
2667 } else {
2668 // Filter on the unitary flag/date lock on each record
2669 $sql .= " AND date_validated IS NULL"; // not locked
2670 }
2671
2672 $sql .= " GROUP BY YEAR(b.doc_date)";
2673 $sql .= $this->db->order("year", 'ASC');
2674
2675 dol_syslog(__METHOD__, LOG_DEBUG);
2676 $resql = $this->db->query($sql);
2677 if (!$resql) {
2678 $this->errors[] = $this->db->lasterror();
2679 return -1;
2680 }
2681
2682 while ($obj = $this->db->fetch_object($resql)) {
2683 $total += (int) $obj->total;
2684 $year_list = array(
2685 'year' => (int) $obj->year,
2686 'count' => array(),
2687 'total' => (int) $obj->total,
2688 );
2689 for ($i = 1; $i <= 12; $i++) {
2690 $year_list['count'][$i] = (int) $obj->{'month' . $i};
2691 }
2692
2693 $list[] = $year_list;
2694 }
2695
2696 $this->db->free($resql);
2697
2698 return array(
2699 'total' => $total,
2700 'list' => $list,
2701 );
2702 }
2703
2711 public function validateMovementForFiscalPeriod($date_start, $date_end)
2712 {
2713 global $conf;
2714
2715 $now = dol_now();
2716
2717 // Specify as export : update field date_validated on selected month/year
2718 $sql = " UPDATE " . $this->db->prefix() . $this->table_element;
2719 $sql .= " SET date_validated = '" . $this->db->idate($now) . "'";
2720 $sql .= " WHERE entity = " . ((int) $conf->entity);
2721 $sql .= " AND DATE(doc_date) >= '" . $this->db->idate($date_start) . "'";
2722 $sql .= " AND DATE(doc_date) <= '" . $this->db->idate($date_end) . "'";
2723 $sql .= " AND date_validated IS NULL";
2724
2725 dol_syslog(__METHOD__, LOG_DEBUG);
2726 $resql = $this->db->query($sql);
2727 if (!$resql) {
2728 $this->errors[] = $this->db->lasterror();
2729 return -1;
2730 }
2731
2732 return 1;
2733 }
2734
2742 public function accountingResult($date_start, $date_end)
2743 {
2744 global $conf;
2745
2746 $this->db->begin();
2747
2748 $income_statement_amount = 0;
2749
2750 if (getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT')) {
2751 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2752
2753 $pcg_type_filter = array();
2754 foreach ($accounting_groups_used_for_income_statement as $item) {
2755 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2756 }
2757
2758 $sql = 'SELECT';
2759 $sql .= " t.numero_compte,";
2760 $sql .= " aa.pcg_type,";
2761 $sql .= " (SUM(t.credit) - SUM(t.debit)) as accounting_result";
2762 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
2763 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2764 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2765 $sql .= " AND aa.entity = " . ((int) $conf->entity);
2766 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM ' . $this->db->prefix() . 'accounting_system WHERE rowid = ' . ((int) getDolGlobalInt('CHARTOFACCOUNTS')) . ')';
2767 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2768 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
2769 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
2770 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2771
2772 $resql = $this->db->query($sql);
2773 if (!$resql) {
2774 $this->errors[] = 'Error ' . $this->db->lasterror();
2775 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2776 } else {
2777 while ($obj = $this->db->fetch_object($resql)) {
2778 $income_statement_amount += $obj->accounting_result;
2779 }
2780 }
2781 }
2782
2783 return (string) $income_statement_amount;
2784 }
2785
2795 public function closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account = false, $generate_bookkeeping_records = true)
2796 {
2797 global $conf, $langs, $user;
2798
2799 // Current fiscal period
2800 $fiscal_period_id = max(0, $fiscal_period_id);
2801 if (empty($fiscal_period_id)) {
2802 $langs->load('errors');
2803 $this->errors[] = $langs->trans('ErrorBadParameters');
2804 return -1;
2805 }
2806 $fiscal_period = new Fiscalyear($this->db);
2807 $result = $fiscal_period->fetch($fiscal_period_id);
2808 if ($result < 0) {
2809 $this->error = $fiscal_period->error;
2810 $this->errors = $fiscal_period->errors;
2811 return -1;
2812 } elseif (empty($fiscal_period->id)) {
2813 $langs->loadLangs(array('errors', 'compta'));
2814 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2815 return -1;
2816 }
2817
2818 // New fiscal period
2819 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2820 if (empty($new_fiscal_period_id)) {
2821 $langs->load('errors');
2822 $this->errors[] = $langs->trans('ErrorBadParameters');
2823 return -1;
2824 }
2825 $new_fiscal_period = new Fiscalyear($this->db);
2826 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2827 if ($result < 0) {
2828 $this->error = $new_fiscal_period->error;
2829 $this->errors = $new_fiscal_period->errors;
2830 return -1;
2831 } elseif (empty($new_fiscal_period->id)) {
2832 $langs->loadLangs(array('errors', 'compta'));
2833 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
2834 return -1;
2835 }
2836
2837 $error = 0;
2838 $this->db->begin();
2839
2840 $fiscal_period->statut = Fiscalyear::STATUS_CLOSED;
2841 $fiscal_period->status = Fiscalyear::STATUS_CLOSED; // Actually not used
2842 $result = $fiscal_period->update($user);
2843 if ($result < 0) {
2844 $this->error = $fiscal_period->error;
2845 $this->errors = $fiscal_period->errors;
2846 $error++;
2847 }
2848
2849 if (!$error && !empty($generate_bookkeeping_records)) {
2850 $journal_id = max(0, getDolGlobalString('ACCOUNTING_CLOSURE_DEFAULT_JOURNAL'));
2851 if (empty($journal_id)) {
2852 $langs->loadLangs(array('errors', 'accountancy'));
2853 $this->errors[] = $langs->trans('ErrorBadParameters') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2854 $error++;
2855 }
2856
2857 // Fetch journal
2858 if (!$error) {
2859 $journal = new AccountingJournal($this->db);
2860 $result = $journal->fetch($journal_id);
2861 if ($result < 0) {
2862 $this->error = $journal->error;
2863 $this->errors = $journal->errors;
2864 $error++;
2865 } elseif ($result == 0) {
2866 $langs->loadLangs(array('errors', 'accountancy'));
2867 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2868 $error++;
2869 }
2870 } else {
2871 $journal = null;
2872 }
2873
2874 if (!$error && is_object($journal)) {
2875 $accounting_groups_used_for_balance_sheet_account = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_BALANCE_SHEET_ACCOUNT'))), 'strlen');
2876 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2877
2878 $pcg_type_filter = array();
2879 $tmp = array_merge($accounting_groups_used_for_balance_sheet_account, $accounting_groups_used_for_income_statement);
2880 foreach ($tmp as $item) {
2881 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2882 }
2883
2884 $sql = 'SELECT';
2885 $sql .= " t.numero_compte,";
2886 if ($separate_auxiliary_account) {
2887 $sql .= " NULLIF(t.subledger_account, '') as subledger_account,"; // fix db issues with Null or "" values
2888 }
2889 $sql .= " aa.pcg_type,";
2890 $sql .= " (SUM(t.credit) - SUM(t.debit)) as opening_balance";
2891 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
2892 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2893 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2894 $sql .= " AND aa.entity = ". ((int) $conf->entity);
2895 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM '.$this->db->prefix().'accounting_system WHERE rowid = '.((int) getDolGlobalInt('CHARTOFACCOUNTS')).')';
2896 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2897 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
2898 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
2899 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2900 if ($separate_auxiliary_account) {
2901 $sql .= " , NULLIF(t.subledger_account, '')";
2902 }
2903 $sql .= ' HAVING (SUM(t.credit) - SUM(t.debit)) != 0 '; // Exclude rows with opening_balance = 0
2904 $sql .= $this->db->order("t.numero_compte", "ASC");
2905
2906 $resql = $this->db->query($sql);
2907 if (!$resql) {
2908 $this->errors[] = 'Error ' . $this->db->lasterror();
2909 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2910
2911 $error++;
2912 } else {
2913 $now = dol_now();
2914 $income_statement_amount = 0;
2915 while ($obj = $this->db->fetch_object($resql)) {
2916 if (in_array($obj->pcg_type, $accounting_groups_used_for_income_statement)) {
2917 $income_statement_amount += $obj->opening_balance;
2918 } else {
2919 // Insert bookkeeping record for balance sheet account
2920 $mt = $obj->opening_balance;
2921
2922 $bookkeeping = new BookKeeping($this->db);
2923 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2924
2925 $bookkeeping->date_lim_reglement = '';
2926 $bookkeeping->doc_ref = $fiscal_period->label;
2927
2928 $bookkeeping->date_creation = $now;
2929 $bookkeeping->doc_type = 'closure';
2930 $bookkeeping->fk_doc = $fiscal_period->id;
2931 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2932 $bookkeeping->thirdparty_code = '';
2933
2934 if ($separate_auxiliary_account) {
2935 $bookkeeping->subledger_account = $obj->subledger_account;
2936 $sql = 'SELECT';
2937 $sql .= " subledger_label";
2938 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
2939 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
2940 $sql .= " ORDER BY doc_date DESC";
2941 $sql .= " LIMIT 1";
2942 $result = $this->db->query($sql);
2943 if (!$result) {
2944 $this->errors[] = 'Error: ' . $this->db->lasterror();
2945 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2946 $error++;
2947 }
2948 $objtmp = $this->db->fetch_object($result);
2949 $bookkeeping->subledger_label = $objtmp->subledger_label; // latest subledger label used
2950 } else {
2951 $bookkeeping->subledger_account = null;
2952 $bookkeeping->subledger_label = null;
2953 }
2954
2955 $bookkeeping->numero_compte = $obj->numero_compte;
2956 $accountingaccount = new AccountingAccount($this->db);
2957 $accountingaccount->fetch(0, $obj->numero_compte);
2958 $bookkeeping->label_compte = $accountingaccount->label; // latest account label used
2959
2960 $bookkeeping->label_operation = $new_fiscal_period->label;
2961 $bookkeeping->montant = $mt;
2962 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2963 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2964 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2965 $bookkeeping->code_journal = $journal->code;
2966 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2967 $bookkeeping->fk_user_author = $user->id;
2968 $bookkeeping->entity = $conf->entity;
2969
2970 $result = $bookkeeping->create($user);
2971 if ($result < 0) {
2972 $this->setErrorsFromObject($bookkeeping);
2973 $error++;
2974 break;
2975 }
2976 }
2977 }
2978
2979 // Insert bookkeeping record for income statement
2980 if (!$error && $income_statement_amount != 0) {
2981 $mt = $income_statement_amount;
2982 $accountingaccount = new AccountingAccount($this->db);
2983 $accountingaccount->fetch(0, getDolGlobalString($income_statement_amount < 0 ? 'ACCOUNTING_RESULT_LOSS' : 'ACCOUNTING_RESULT_PROFIT'), true);
2984
2985 $bookkeeping = new BookKeeping($this->db);
2986 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2987
2988 $bookkeeping->date_lim_reglement = '';
2989 $bookkeeping->doc_ref = $fiscal_period->label;
2990
2991 $bookkeeping->date_creation = $now;
2992 $bookkeeping->doc_type = 'closure';
2993 $bookkeeping->fk_doc = $fiscal_period->id;
2994 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2995 $bookkeeping->thirdparty_code = '';
2996
2997 if ($separate_auxiliary_account) {
2998 $bookkeeping->subledger_account = $obj->subledger_account;
2999 $sql = 'SELECT';
3000 $sql .= " subledger_label";
3001 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
3002 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
3003 $sql .= " ORDER BY doc_date DESC";
3004 $sql .= " LIMIT 1";
3005 $result = $this->db->query($sql);
3006 if (!$result) {
3007 $this->errors[] = 'Error: ' . $this->db->lasterror();
3008 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3009 $error++;
3010 }
3011 $objtmp = $this->db->fetch_object($result);
3012 $bookkeeping->subledger_label = $objtmp->subledger_label ?? null; // latest subledger label used
3013 } else {
3014 $bookkeeping->subledger_account = null;
3015 $bookkeeping->subledger_label = null;
3016 }
3017
3018 $bookkeeping->numero_compte = $accountingaccount->account_number;
3019 $bookkeeping->label_compte = $accountingaccount->label;
3020
3021 $bookkeeping->label_operation = $new_fiscal_period->label;
3022 $bookkeeping->montant = $mt;
3023 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
3024 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
3025 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
3026 $bookkeeping->code_journal = $journal->code;
3027 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
3028 $bookkeeping->fk_user_author = $user->id;
3029 $bookkeeping->entity = $conf->entity;
3030
3031 $result = $bookkeeping->create($user);
3032 if ($result < 0) {
3033 $this->setErrorsFromObject($bookkeeping);
3034 $error++;
3035 }
3036 }
3037 $this->db->free($resql);
3038 }
3039 }
3040 }
3041
3042 if ($error) {
3043 $this->db->rollback();
3044 return -1;
3045 } else {
3046 $this->db->commit();
3047 return 1;
3048 }
3049 }
3050
3061 public function insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
3062 {
3063 global $conf, $langs, $user;
3064
3065 // Current fiscal period
3066 $fiscal_period_id = max(0, $fiscal_period_id);
3067 if (empty($fiscal_period_id)) {
3068 $langs->load('errors');
3069 $this->errors[] = $langs->trans('ErrorBadParameters');
3070 return -1;
3071 }
3072 $fiscal_period = new Fiscalyear($this->db);
3073 $result = $fiscal_period->fetch($fiscal_period_id);
3074 if ($result < 0) {
3075 $this->error = $fiscal_period->error;
3076 $this->errors = $fiscal_period->errors;
3077 return -1;
3078 } elseif (empty($fiscal_period->id)) {
3079 $langs->loadLangs(array('errors', 'compta'));
3080 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
3081 return -1;
3082 }
3083
3084 // New fiscal period
3085 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
3086 if (empty($new_fiscal_period_id)) {
3087 $langs->load('errors');
3088 $this->errors[] = $langs->trans('ErrorBadParameters');
3089 return -1;
3090 }
3091 $new_fiscal_period = new Fiscalyear($this->db);
3092 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
3093 if ($result < 0) {
3094 $this->error = $new_fiscal_period->error;
3095 $this->errors = $new_fiscal_period->errors;
3096 return -1;
3097 } elseif (empty($new_fiscal_period->id)) {
3098 $langs->loadLangs(array('errors', 'compta'));
3099 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
3100 return -1;
3101 }
3102
3103 // Inventory journal
3104 $inventory_journal_id = max(0, $inventory_journal_id);
3105 if (empty($inventory_journal_id)) {
3106 $langs->load('errors');
3107 $this->errors[] = $langs->trans('ErrorBadParameters');
3108 return -1;
3109 }
3110 // Fetch journal
3111 $inventory_journal = new AccountingJournal($this->db);
3112 $result = $inventory_journal->fetch($inventory_journal_id);
3113 if ($result < 0) {
3114 $this->error = $inventory_journal->error;
3115 $this->errors = $inventory_journal->errors;
3116 return -1;
3117 } elseif ($result == 0) {
3118 $langs->loadLangs(array('errors', 'accountancy'));
3119 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('InventoryJournal');
3120 return -1;
3121 }
3122
3123 $error = 0;
3124 $this->db->begin();
3125
3126 $sql = 'SELECT t.rowid';
3127 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
3128 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
3129 $sql .= " AND code_journal = '" . $this->db->escape($inventory_journal->code) . "'";
3130 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
3131 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
3132 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
3133 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
3134
3135 $resql = $this->db->query($sql);
3136 if (!$resql) {
3137 $this->errors[] = 'Error ' . $this->db->lasterror();
3138 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3139
3140 $error++;
3141 } else {
3142 $now = dol_now();
3143 while ($obj = $this->db->fetch_object($resql)) {
3144 $bookkeeping = new BookKeeping($this->db);
3145 $result = $bookkeeping->fetch($obj->rowid);
3146 if ($result < 0) {
3147 $this->error = $inventory_journal->error;
3148 $this->errors = $inventory_journal->errors;
3149 $error++;
3150 break;
3151 } elseif ($result == 0) {
3152 $langs->loadLangs(array('errors', 'accountancy'));
3153 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('LineId') . ': ' . $obj->rowid;
3154 $error++;
3155 break;
3156 }
3157
3158 $bookkeeping->id = 0;
3159 $bookkeeping->doc_date = $new_fiscal_period->date_start;
3160 $bookkeeping->doc_ref = $new_fiscal_period->label;
3161 $bookkeeping->date_creation = $now;
3162 $bookkeeping->doc_type = 'accounting_reversal';
3163 $bookkeeping->fk_doc = $new_fiscal_period->id;
3164 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3165
3166 $bookkeeping->montant = -$bookkeeping->montant;
3167 $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
3168 $old_debit = $bookkeeping->debit;
3169 $bookkeeping->debit = $bookkeeping->credit;
3170 $bookkeeping->credit = $old_debit;
3171
3172 $bookkeeping->fk_user_author = $user->id;
3173 $bookkeeping->entity = $conf->entity;
3174
3175 $result = $bookkeeping->create($user);
3176 if ($result < 0) {
3177 $this->error = $bookkeeping->error;
3178 $this->errors = $bookkeeping->errors;
3179 $error++;
3180 break;
3181 }
3182 }
3183 $this->db->free($resql);
3184 }
3185
3186 if ($error) {
3187 $this->db->rollback();
3188 return -1;
3189 } else {
3190 $this->db->commit();
3191 return 1;
3192 }
3193 }
3194}
3195
3200{
3204 public $id;
3205
3209 public $doc_date = null;
3213 public $doc_type;
3214
3218 public $doc_ref;
3219
3223 public $fk_doc;
3224
3228 public $fk_docdet;
3229
3233 public $thirdparty_code;
3234
3238 public $subledger_account;
3239
3243 public $subledger_label;
3244
3248 public $numero_compte;
3249
3253 public $label_compte;
3254
3258 public $label_operation;
3259
3263 public $debit;
3264
3268 public $credit;
3269
3274 public $montant;
3275
3279 public $amount;
3280
3284 public $multicurrency_amount;
3285
3289 public $multicurrency_code;
3290
3294 public $sens;
3295
3299 public $lettering_code;
3300
3304 public $date_lettering;
3305
3309 public $fk_user_author;
3310
3311
3315 public $import_key;
3316
3320 public $code_journal;
3321
3325 public $journal_label;
3329 public $piece_num;
3330
3334 public $date_export;
3335
3339 public $date_lim_reglement;
3340}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
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:459
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 movement number.
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.
fetchPerMvt($piecenum, $mode='')
Load an accounting document into memory from database.
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.
deleteMvtNum($piecenum, $mode='')
Delete bookkeeping by piece number.
validBookkeepingDate($date)
Is the bookkeeping date valid (on an open period or not on a closed period) ?
deleteByYearAndJournal($delyear=0, $journal='', $mode='', $delmonth=0)
Delete bookkeeping by year.
loadFiscalPeriods($force=false, $mode='active')
Load list of active fiscal period.
fetchAllByAccount($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND', $option=0, $countonly=0)
Load object in memory from the database in ->lines.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
getCanModifyBookkeepingSQL($alias='', $force=false)
Get SQL string for check if the bookkeeping can be modified or deleted ? (cached)
fetch($id, $ref=null, $mode='')
Load object in memory from the database.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND', $showAlreadyExportMovements=1)
Load object in memory from the database.
accountingResult($date_start, $date_end)
Define accounting result.
canModifyBookkeeping($id, $mode='')
Is the bookkeeping can be modified or deleted ?
fetchAllPerMvt($piecenum, $mode='')
Load all accounting lines related to a given transaction ID $piecenum.
validateMovementForFiscalPeriod($date_start, $date_end)
Validate all movement between the specified dates.
export_bookkeeping($model='ebp')
Export bookkeeping.
Class BookKeepingLine.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class to manage fiscal year.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Class to manage Dolibarr users.
dolSqlDateFilter($datefield, $day_date, $month_date, $year_date, $excludefirstand=0, $gm=false)
Generate a SQL string to make a filter into a range (for second of date until last second of date).
Definition date.lib.php:382
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79