dolibarr 21.0.0-beta
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 }
236 if (isset($this->fk_doc)) {
237 $this->fk_doc = (int) $this->fk_doc;
238 }
239 if (isset($this->fk_docdet)) {
240 $this->fk_docdet = (int) $this->fk_docdet;
241 }
242 if (isset($this->thirdparty_code)) {
243 $this->thirdparty_code = trim($this->thirdparty_code);
244 }
245 if (isset($this->subledger_account)) {
246 $this->subledger_account = trim($this->subledger_account);
247 }
248 if (isset($this->subledger_label)) {
249 $this->subledger_label = trim($this->subledger_label);
250 }
251 if (isset($this->numero_compte)) {
252 $this->numero_compte = trim($this->numero_compte);
253 }
254 if (isset($this->label_compte)) {
255 $this->label_compte = trim($this->label_compte);
256 }
257 if (isset($this->label_operation)) {
258 $this->label_operation = trim($this->label_operation);
259 }
260 if (isset($this->debit)) {
261 $this->debit = (float) $this->debit;
262 }
263 if (isset($this->credit)) {
264 $this->credit = (float) $this->credit;
265 }
266 if (isset($this->montant)) {
267 $this->montant = (float) $this->montant;
268 }
269 if (isset($this->amount)) {
270 $this->amount = (float) $this->amount;
271 }
272 if (isset($this->sens)) {
273 $this->sens = trim($this->sens);
274 }
275 if (isset($this->import_key)) {
276 $this->import_key = trim($this->import_key);
277 }
278 if (isset($this->code_journal)) {
279 $this->code_journal = trim($this->code_journal);
280 }
281 if (isset($this->journal_label)) {
282 $this->journal_label = trim($this->journal_label);
283 }
284 if (isset($this->piece_num)) {
285 $this->piece_num = (int) $this->piece_num;
286 }
287 if (empty($this->debit)) {
288 $this->debit = 0.0;
289 }
290 if (empty($this->credit)) {
291 $this->credit = 0.0;
292 }
293
294 $result = $this->validBookkeepingDate($this->doc_date);
295 if ($result < 0) {
296 return -1;
297 } elseif ($result == 0) {
298 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
299 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
300 } else {
301 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
302 }
303 return -1;
304 }
305
306 // Check parameters
307 if (($this->numero_compte == "") || $this->numero_compte == '-1' || $this->numero_compte == 'NotDefined') {
308 $langs->loadLangs(array("errors"));
309 if (in_array($this->doc_type, array('bank', 'expense_report'))) {
310 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet, $this->doc_type);
311 } else {
312 //$this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->doc_ref, $this->label_compte);
313 $mesg = $this->doc_ref.', '.$langs->trans("AccountAccounting").': '.($this->numero_compte != -1 ? $this->numero_compte : $langs->trans("Unknown"));
314 if ($this->subledger_account && $this->subledger_account != $this->numero_compte) {
315 $mesg .= ', '.$langs->trans("SubledgerAccount").': '.$this->subledger_account;
316 }
317 $this->errors[] = $langs->trans('ErrorFieldAccountNotDefinedForLine', $mesg);
318 }
319
320 return -1;
321 }
322
323 $this->db->begin();
324
325 $this->piece_num = 0;
326
327 // First check if line not yet already in bookkeeping.
328 // 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
329 // with same doc_type, fk_doc, numero_compte for 1 invoice line when using localtaxes with same account)
330 // WARNING: This is not reliable, label may have been modified. This is just a small protection.
331 // The page that make transfer make the test on couple (doc_type - fk_doc) only.
332 $sql = "SELECT count(*) as nb";
333 $sql .= " FROM ".$this->db->prefix().$this->table_element;
334 $sql .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'";
335 $sql .= " AND fk_doc = ".((int) $this->fk_doc);
336 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
337 // DO NOT USE THIS IN PRODUCTION. This will generate a lot of trouble into reports and will corrupt database (by generating duplicate entries.
338 $sql .= " AND fk_docdet = ".((int) $this->fk_docdet); // This field can be 0 if record is for several lines
339 }
340 $sql .= " AND numero_compte = '".$this->db->escape($this->numero_compte)."'";
341 $sql .= " AND label_operation = '".$this->db->escape($this->label_operation)."'";
342 if (!empty($this->subledger_account)) {
343 $sql .= " AND subledger_account = '".$this->db->escape($this->subledger_account)."'";
344 }
345 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
346
347 $resql = $this->db->query($sql);
348
349 if ($resql) {
350 $row = $this->db->fetch_object($resql);
351 if ($row->nb == 0) { // Not already into bookkeeping
352 // Check to know if piece_num already exists for data we try to insert to reuse the same value
353 $sqlnum = "SELECT piece_num";
354 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
355 $sqlnum .= " WHERE doc_type = '".$this->db->escape($this->doc_type)."'"; // For example doc_type = 'bank'
356 $sqlnum .= " AND fk_doc = ".((int) $this->fk_doc);
357 if (getDolGlobalString('ACCOUNTANCY_ENABLE_FKDOCDET')) {
358 // fk_docdet is rowid into llx_bank or llx_facturedet or llx_facturefourndet, or ...
359 $sqlnum .= " AND fk_docdet = ".((int) $this->fk_docdet);
360 }
361 $sqlnum .= " AND doc_ref = '".$this->db->escape($this->doc_ref)."'"; // ref of source object
362 $sqlnum .= " AND entity = ".$conf->entity; // Do not use getEntity for accounting features
363
364 dol_syslog(get_class($this).":: create sqlnum=".$sqlnum, LOG_DEBUG);
365 $resqlnum = $this->db->query($sqlnum);
366 if ($resqlnum) {
367 $objnum = $this->db->fetch_object($resqlnum);
368 $this->piece_num = $objnum->piece_num;
369 }
370
371 dol_syslog(get_class($this)."::create this->piece_num=".$this->piece_num, LOG_DEBUG);
372 if (empty($this->piece_num)) {
373 $sqlnum = "SELECT MAX(piece_num)+1 as maxpiecenum";
374 $sqlnum .= " FROM ".$this->db->prefix().$this->table_element;
375 $sqlnum .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
376
377 $resqlnum = $this->db->query($sqlnum);
378 if ($resqlnum) {
379 $objnum = $this->db->fetch_object($resqlnum);
380 $this->piece_num = $objnum->maxpiecenum;
381 }
382 dol_syslog(get_class($this).":: create now this->piece_num=".$this->piece_num, LOG_DEBUG);
383 }
384 if (empty($this->piece_num)) {
385 $this->piece_num = 1;
386 }
387
388 $now = dol_now();
389
390 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element." (";
391 $sql .= "doc_date";
392 $sql .= ", date_lim_reglement";
393 $sql .= ", doc_type";
394 $sql .= ", doc_ref";
395 $sql .= ", fk_doc";
396 $sql .= ", fk_docdet";
397 $sql .= ", thirdparty_code";
398 $sql .= ", subledger_account";
399 $sql .= ", subledger_label";
400 $sql .= ", numero_compte";
401 $sql .= ", label_compte";
402 $sql .= ", label_operation";
403 $sql .= ", debit";
404 $sql .= ", credit";
405 $sql .= ", montant";
406 $sql .= ", sens";
407 $sql .= ", fk_user_author";
408 $sql .= ", date_creation";
409 $sql .= ", code_journal";
410 $sql .= ", journal_label";
411 $sql .= ", piece_num";
412 $sql .= ', entity';
413 $sql .= ") VALUES (";
414 $sql .= "'".$this->db->idate($this->doc_date)."'";
415 $sql .= ", ".(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL');
416 $sql .= ", '".$this->db->escape($this->doc_type)."'";
417 $sql .= ", '".$this->db->escape($this->doc_ref)."'";
418 $sql .= ", ".((int) $this->fk_doc);
419 $sql .= ", ".((int) $this->fk_docdet);
420 $sql .= ", ".(!empty($this->thirdparty_code) ? ("'".$this->db->escape($this->thirdparty_code)."'") : "NULL");
421 $sql .= ", ".(!empty($this->subledger_account) ? ("'".$this->db->escape($this->subledger_account)."'") : "NULL");
422 $sql .= ", ".(!empty($this->subledger_label) ? ("'".$this->db->escape($this->subledger_label)."'") : "NULL");
423 $sql .= ", '".$this->db->escape($this->numero_compte)."'";
424 $sql .= ", ".(!empty($this->label_compte) ? ("'".$this->db->escape($this->label_compte)."'") : "NULL");
425 $sql .= ", '".$this->db->escape($this->label_operation)."'";
426 $sql .= ", ".((float) $this->debit);
427 $sql .= ", ".((float) $this->credit);
428 $sql .= ", ".((float) $this->montant);
429 $sql .= ", ".(!empty($this->sens) ? ("'".$this->db->escape($this->sens)."'") : "NULL");
430 $sql .= ", '".$this->db->escape($this->fk_user_author)."'";
431 $sql .= ", '".$this->db->idate($now)."'";
432 $sql .= ", '".$this->db->escape($this->code_journal)."'";
433 $sql .= ", ".(!empty($this->journal_label) ? ("'".$this->db->escape($this->journal_label)."'") : "NULL");
434 $sql .= ", ".((int) $this->piece_num);
435 $sql .= ", ".(!isset($this->entity) ? $conf->entity : $this->entity);
436 $sql .= ")";
437
438 $resql = $this->db->query($sql);
439 if ($resql) {
440 $id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
441
442 if ($id > 0) {
443 $this->id = $id;
444 $result = 0;
445 } else {
446 $result = -2;
447 $error++;
448 $this->errors[] = 'Error Create Error '.$result.' lecture ID';
449 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
450 }
451 } else {
452 $result = -1;
453 $error++;
454 $this->errors[] = 'Error '.$this->db->lasterror();
455 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
456 }
457 } else { // Already exists
458 $result = -3;
459 $error++;
460 $this->error = 'BookkeepingRecordAlreadyExists';
461 dol_syslog(__METHOD__.' '.$this->error, LOG_WARNING);
462 }
463 } else {
464 $result = -5;
465 $error++;
466 $this->errors[] = 'Error '.$this->db->lasterror();
467 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
468 }
469
470 // Call triggers
471 if (! $error && ! $notrigger) {
472 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
473 if ($result < 0) {
474 $error++;
475 }
476 }
477
478 // Commit or rollback
479 if ($error) {
480 $this->db->rollback();
481 return -1 * $error;
482 } else {
483 $this->db->commit();
484 return $result;
485 }
486 }
487
498 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
499 {
500 global $db, $conf, $langs;
501 global $dolibarr_main_authentication, $dolibarr_main_demo;
502 global $menumanager, $hookmanager;
503
504 if (!empty($conf->dol_no_mouse_hover)) {
505 $notooltip = 1; // Force disable tooltips
506 }
507
508 $result = '';
509 $companylink = '';
510
511 $label = '<u>'.$langs->trans("Transaction").'</u>';
512 $label .= '<br>';
513 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->piece_num;
514
515 $url = DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?piece_num='.$this->piece_num;
516
517 if ($option != 'nolink') {
518 // Add param to save lastsearch_values or not
519 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
520 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
521 $add_save_lastsearch_values = 1;
522 }
523 if ($add_save_lastsearch_values) {
524 $url .= '&save_lastsearch_values=1';
525 }
526 }
527
528 $linkclose = '';
529 if (empty($notooltip)) {
530 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
531 $label = $langs->trans("ShowTransaction");
532 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
533 }
534 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
535 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
536 } else {
537 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
538 }
539
540 $linkstart = '<a href="'.$url.'"';
541 $linkstart .= $linkclose.'>';
542 $linkend = '</a>';
543
544 $result .= $linkstart;
545 if ($withpicto) {
546 $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);
547 }
548 if ($withpicto != 2) {
549 $result .= $this->piece_num;
550 }
551 $result .= $linkend;
552 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
553
554 global $action;
555 $hookmanager->initHooks(array($this->element . 'dao'));
556 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
557 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
558 if ($reshook > 0) {
559 $result = $hookmanager->resPrint;
560 } else {
561 $result .= $hookmanager->resPrint;
562 }
563 return $result;
564 }
565
574 public function createStd(User $user, $notrigger = 0, $mode = '')
575 {
576 global $conf, $langs;
577
578 $langs->loadLangs(array("accountancy", "bills", "compta"));
579
580 dol_syslog(__METHOD__, LOG_DEBUG);
581
582 $error = 0;
583
584 // Clean parameters
585 if (isset($this->doc_type)) {
586 $this->doc_type = trim($this->doc_type);
587 }
588 if (isset($this->doc_ref)) {
589 $this->doc_ref = trim($this->doc_ref);
590 }
591 if (isset($this->fk_doc)) {
592 $this->fk_doc = (int) $this->fk_doc;
593 }
594 if (isset($this->fk_docdet)) {
595 $this->fk_docdet = (int) $this->fk_docdet;
596 }
597 if (isset($this->thirdparty_code)) {
598 $this->thirdparty_code = trim($this->thirdparty_code);
599 }
600 if (isset($this->subledger_account)) {
601 $this->subledger_account = trim($this->subledger_account);
602 }
603 if (isset($this->subledger_label)) {
604 $this->subledger_label = trim($this->subledger_label);
605 }
606 if (isset($this->numero_compte)) {
607 $this->numero_compte = trim($this->numero_compte);
608 }
609 if (isset($this->label_compte)) {
610 $this->label_compte = trim($this->label_compte);
611 }
612 if (isset($this->label_operation)) {
613 $this->label_operation = trim($this->label_operation);
614 }
615 if (isset($this->sens)) {
616 $this->sens = trim($this->sens);
617 }
618 if (isset($this->import_key)) {
619 $this->import_key = trim($this->import_key);
620 }
621 if (isset($this->code_journal)) {
622 $this->code_journal = trim($this->code_journal);
623 }
624 if (isset($this->journal_label)) {
625 $this->journal_label = trim($this->journal_label);
626 }
627 if (isset($this->piece_num)) {
628 $this->piece_num = (int) $this->piece_num;
629 }
630 if (empty($this->debit)) {
631 $this->debit = 0;
632 }
633 if (empty($this->credit)) {
634 $this->credit = 0;
635 }
636 if (empty($this->montant)) {
637 $this->montant = 0;
638 }
639
640 $result = $this->validBookkeepingDate($this->doc_date);
641 if ($result < 0) {
642 return -1;
643 } elseif ($result == 0) {
644 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
645 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
646 } else {
647 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
648 }
649 return -1;
650 }
651
652 $this->debit = (float) price2num($this->debit, 'MT');
653 $this->credit = (float) price2num($this->credit, 'MT');
654 $this->montant = (float) price2num($this->montant, 'MT');
655
656 $now = dol_now();
657
658 // Check parameters
659 $this->journal_label = $langs->trans($this->journal_label);
660
661 // Insert request
662 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.$mode.' (';
663 $sql .= 'doc_date,';
664 $sql .= 'date_lim_reglement,';
665 $sql .= 'doc_type,';
666 $sql .= 'doc_ref,';
667 $sql .= 'fk_doc,';
668 $sql .= 'fk_docdet,';
669 $sql .= 'thirdparty_code,';
670 $sql .= 'subledger_account,';
671 $sql .= 'subledger_label,';
672 $sql .= 'numero_compte,';
673 $sql .= 'label_compte,';
674 $sql .= 'label_operation,';
675 $sql .= 'debit,';
676 $sql .= 'credit,';
677 $sql .= 'montant,';
678 $sql .= 'sens,';
679 $sql .= 'fk_user_author,';
680 $sql .= 'date_creation,';
681 $sql .= 'code_journal,';
682 $sql .= 'journal_label,';
683 $sql .= 'piece_num,';
684 $sql .= 'entity';
685 $sql .= ') VALUES (';
686 $sql .= ' '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'NULL').',';
687 $sql .= ' '.(isDolTms($this->date_lim_reglement) ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'NULL').',';
688 $sql .= ' '.(!isset($this->doc_type) ? 'NULL' : "'".$this->db->escape($this->doc_type)."'").',';
689 $sql .= ' '.(!isset($this->doc_ref) ? 'NULL' : "'".$this->db->escape($this->doc_ref)."'").',';
690 $sql .= ' '.(empty($this->fk_doc) ? '0' : (int) $this->fk_doc).',';
691 $sql .= ' '.(empty($this->fk_docdet) ? '0' : (int) $this->fk_docdet).',';
692 $sql .= ' '.(!isset($this->thirdparty_code) ? 'NULL' : "'".$this->db->escape($this->thirdparty_code)."'").',';
693 $sql .= ' '.(!isset($this->subledger_account) ? 'NULL' : "'".$this->db->escape($this->subledger_account)."'").',';
694 $sql .= ' '.(!isset($this->subledger_label) ? 'NULL' : "'".$this->db->escape($this->subledger_label)."'").',';
695 $sql .= ' '.(!isset($this->numero_compte) ? 'NULL' : "'".$this->db->escape($this->numero_compte)."'").',';
696 $sql .= ' '.(!isset($this->label_compte) ? 'NULL' : "'".$this->db->escape($this->label_compte)."'").',';
697 $sql .= ' '.(!isset($this->label_operation) ? 'NULL' : "'".$this->db->escape($this->label_operation)."'").',';
698 $sql .= ' '.(!isset($this->debit) ? 'NULL' : $this->debit).',';
699 $sql .= ' '.(!isset($this->credit) ? 'NULL' : $this->credit).',';
700 $sql .= ' '.(!isset($this->montant) ? 'NULL' : $this->montant).',';
701 $sql .= ' '.(!isset($this->sens) ? 'NULL' : "'".$this->db->escape($this->sens)."'").',';
702 $sql .= ' '.((int) $user->id).',';
703 $sql .= ' '."'".$this->db->idate($now)."',";
704 $sql .= ' '.(empty($this->code_journal) ? 'NULL' : "'".$this->db->escape($this->code_journal)."'").',';
705 $sql .= ' '.(empty($this->journal_label) ? 'NULL' : "'".$this->db->escape($this->journal_label)."'").',';
706 $sql .= ' '.(empty($this->piece_num) ? 'NULL' : $this->db->escape($this->piece_num)).',';
707 $sql .= ' '.(!isset($this->entity) ? $conf->entity : $this->entity);
708 $sql .= ')';
709
710 $this->db->begin();
711
712 $resql = $this->db->query($sql);
713 if (!$resql) {
714 $error++;
715 $this->errors[] = 'Error '.$this->db->lasterror();
716 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
717 }
718
719 if (!$error) {
720 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element.$mode);
721 // Call triggers
722 if (! $notrigger) {
723 $result = $this->call_trigger('BOOKKEEPING_CREATE', $user);
724 if ($result < 0) {
725 $error++;
726 }
727 }
728 }
729
730 // Commit or rollback
731 if ($error) {
732 $this->db->rollback();
733
734 return -1 * $error;
735 } else {
736 $this->db->commit();
737
738 return $this->id;
739 }
740 }
741
750 public function fetch($id, $ref = null, $mode = '')
751 {
752 global $conf;
753
754 dol_syslog(__METHOD__, LOG_DEBUG);
755
756 $sql = 'SELECT';
757 $sql .= ' t.rowid,';
758 $sql .= " t.doc_date,";
759 $sql .= " t.date_lim_reglement,";
760 $sql .= " t.doc_type,";
761 $sql .= " t.doc_ref,";
762 $sql .= " t.fk_doc,";
763 $sql .= " t.fk_docdet,";
764 $sql .= " t.thirdparty_code,";
765 $sql .= " t.subledger_account,";
766 $sql .= " t.subledger_label,";
767 $sql .= " t.numero_compte,";
768 $sql .= " t.label_compte,";
769 $sql .= " t.label_operation,";
770 $sql .= " t.debit,";
771 $sql .= " t.credit,";
772 $sql .= " t.montant as amount,";
773 $sql .= " t.sens,";
774 $sql .= " t.fk_user_author,";
775 $sql .= " t.import_key,";
776 $sql .= " t.code_journal,";
777 $sql .= " t.journal_label,";
778 $sql .= " t.piece_num,";
779 $sql .= " t.date_creation,";
780 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
781 if ($mode != "_tmp") {
782 $sql .= " t.date_export,";
783 }
784 $sql .= " t.date_validated as date_validation";
785 $sql .= ' FROM '.$this->db->prefix().$this->table_element.$mode.' as t';
786 $sql .= ' WHERE 1 = 1';
787 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
788 if (null !== $ref) {
789 $sql .= " AND t.rowid = ".((int) $ref);
790 } else {
791 $sql .= " AND t.rowid = ".((int) $id);
792 }
793
794 $resql = $this->db->query($sql);
795 if ($resql) {
796 $numrows = $this->db->num_rows($resql);
797 if ($numrows) {
798 $obj = $this->db->fetch_object($resql);
799
800 $this->id = $obj->rowid;
801
802 $this->doc_date = $this->db->jdate($obj->doc_date);
803 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
804 $this->doc_type = $obj->doc_type;
805 $this->doc_ref = $obj->doc_ref;
806 $this->fk_doc = $obj->fk_doc;
807 $this->fk_docdet = $obj->fk_docdet;
808 $this->thirdparty_code = $obj->thirdparty_code;
809 $this->subledger_account = $obj->subledger_account;
810 $this->subledger_label = $obj->subledger_label;
811 $this->numero_compte = $obj->numero_compte;
812 $this->label_compte = $obj->label_compte;
813 $this->label_operation = $obj->label_operation;
814 $this->debit = $obj->debit;
815 $this->credit = $obj->credit;
816 $this->montant = $obj->amount;
817 $this->amount = $obj->amount;
818 $this->sens = $obj->sens;
819 $this->fk_user_author = $obj->fk_user_author;
820 $this->import_key = $obj->import_key;
821 $this->code_journal = $obj->code_journal;
822 $this->journal_label = $obj->journal_label;
823 $this->piece_num = $obj->piece_num;
824 $this->date_creation = $this->db->jdate($obj->date_creation);
825 $this->date_export = $this->db->jdate($obj->date_export);
826 $this->date_validation = isset($obj->date_validation) ? $this->db->jdate($obj->date_validation) : '';
827 }
828 $this->db->free($resql);
829
830 if ($numrows) {
831 return 1;
832 } else {
833 return 0;
834 }
835 } else {
836 $this->errors[] = 'Error '.$this->db->lasterror();
837 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
838
839 return -1;
840 }
841 }
842
843
857 public function fetchAllByAccount($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND', $option = 0, $countonly = 0)
858 {
859 global $conf;
860
861 dol_syslog(__METHOD__, LOG_DEBUG);
862
863 $this->lines = array();
864 $num = 0;
865
866 $sql = 'SELECT';
867 if ($countonly) {
868 $sql .= ' COUNT(t.rowid) as nb';
869 } else {
870 $sql .= ' t.rowid,';
871 $sql .= " t.doc_date,";
872 $sql .= " t.doc_type,";
873 $sql .= " t.doc_ref,";
874 $sql .= " t.fk_doc,";
875 $sql .= " t.fk_docdet,";
876 $sql .= " t.thirdparty_code,";
877 $sql .= " t.subledger_account,";
878 $sql .= " t.subledger_label,";
879 $sql .= " t.numero_compte,";
880 $sql .= " t.label_compte,";
881 $sql .= " t.label_operation,";
882 $sql .= " t.debit,";
883 $sql .= " t.credit,";
884 $sql .= " t.montant as amount,";
885 $sql .= " t.sens,";
886 $sql .= " t.multicurrency_amount,";
887 $sql .= " t.multicurrency_code,";
888 $sql .= " t.lettering_code,";
889 $sql .= " t.date_lettering,";
890 $sql .= " t.fk_user_author,";
891 $sql .= " t.import_key,";
892 $sql .= " t.code_journal,";
893 $sql .= " t.journal_label,";
894 $sql .= " t.piece_num,";
895 $sql .= " t.date_creation,";
896 $sql .= " t.date_export,";
897 $sql .= " t.date_validated as date_validation,";
898 $sql .= " t.date_lim_reglement,";
899 $sql .= " t.import_key";
900 }
901 // Manage filter
902 $sqlwhere = array();
903 if (count($filter) > 0) {
904 foreach ($filter as $key => $value) {
905 if ($key == 't.doc_date>=') {
906 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
907 } elseif ($key == 't.doc_date<=') {
908 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
909 } elseif ($key == 't.doc_date>') {
910 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
911 } elseif ($key == 't.doc_date<') {
912 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
913 } elseif ($key == 't.numero_compte>=') {
914 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
915 } elseif ($key == 't.numero_compte<=') {
916 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
917 } elseif ($key == 't.subledger_account>=') {
918 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
919 } elseif ($key == 't.subledger_account<=') {
920 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
921 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
922 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
923 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
924 $sqlwhere[] = $this->db->sanitize($key).' LIKE \''.$this->db->escape($this->db->escapeforlike($value)).'%\'';
925 } elseif ($key == 't.date_creation>=') {
926 $sqlwhere[] = 't.date_creation >= \''.$this->db->idate($value).'\'';
927 } elseif ($key == 't.date_creation<=') {
928 $sqlwhere[] = 't.date_creation <= \''.$this->db->idate($value).'\'';
929 } elseif ($key == 't.date_export>=') {
930 $sqlwhere[] = 't.date_export >= \''.$this->db->idate($value).'\'';
931 } elseif ($key == 't.date_export<=') {
932 $sqlwhere[] = 't.date_export <= \''.$this->db->idate($value).'\'';
933 } elseif ($key == 't.date_validated>=') {
934 $sqlwhere[] = 't.date_validated >= \''.$this->db->idate($value).'\'';
935 } elseif ($key == 't.date_validated<=') {
936 $sqlwhere[] = 't.date_validated <= \''.$this->db->idate($value).'\'';
937 } elseif ($key == 't.date_lim_reglement>=') {
938 $sqlwhere[] = 't.date_lim_reglement>=\''.$this->db->idate($value).'\'';
939 } elseif ($key == 't.date_lim_reglement<=') {
940 $sqlwhere[] = 't.date_lim_reglement<=\''.$this->db->idate($value).'\'';
941 } elseif ($key == 't.credit' || $key == 't.debit') {
942 $sqlwhere[] = natural_search($key, $value, 1, 1);
943 } elseif ($key == 't.reconciled_option') {
944 $sqlwhere[] = 't.lettering_code IS NULL';
945 } elseif ($key == 't.code_journal' && !empty($value)) {
946 if (is_array($value)) {
947 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
948 } else {
949 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
950 }
951 } elseif ($key == 't.search_accounting_code_in' && !empty($value)) {
952 $sqlwhere[] = 't.numero_compte IN ('.$this->db->sanitize($value, 1).')';
953 } else {
954 $sqlwhere[] = natural_search($key, $value, 0, 1);
955 }
956 }
957 }
958 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
959 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
960 if (count($sqlwhere) > 0) {
961 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
962 }
963 // Filter by ledger account or subledger account
964 if (!empty($option)) {
965 $sql .= " AND t.subledger_account IS NOT NULL";
966 $sql .= " AND t.subledger_account <> ''";
967 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
968 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
969 } else {
970 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
971 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
972 }
973
974 if (!$countonly) {
975 $sql .= $this->db->order($sortfield, $sortorder);
976 if (!empty($limit)) {
977 $sql .= $this->db->plimit($limit + 1, $offset);
978 }
979 }
980
981 $resql = $this->db->query($sql);
982 if ($resql) {
983 if ($countonly) {
984 $obj = $this->db->fetch_object($resql);
985 if ($obj) {
986 $num = $obj->nb;
987 }
988 } else {
989 $num = $this->db->num_rows($resql);
990
991 $i = 0;
992 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
993 $line = new BookKeepingLine($this->db);
994
995 $line->id = $obj->rowid;
996
997 $line->doc_date = $this->db->jdate($obj->doc_date);
998 $line->doc_type = $obj->doc_type;
999 $line->doc_ref = $obj->doc_ref;
1000 $line->fk_doc = $obj->fk_doc;
1001 $line->fk_docdet = $obj->fk_docdet;
1002 $line->thirdparty_code = $obj->thirdparty_code;
1003 $line->subledger_account = $obj->subledger_account;
1004 $line->subledger_label = $obj->subledger_label;
1005 $line->numero_compte = $obj->numero_compte;
1006 $line->label_compte = $obj->label_compte;
1007 $line->label_operation = $obj->label_operation;
1008 $line->debit = $obj->debit;
1009 $line->credit = $obj->credit;
1010 $line->montant = $obj->amount; // deprecated
1011 $line->amount = $obj->amount;
1012 $line->sens = $obj->sens;
1013 $line->multicurrency_amount = $obj->multicurrency_amount;
1014 $line->multicurrency_code = $obj->multicurrency_code;
1015 $line->lettering_code = $obj->lettering_code;
1016 $line->date_lettering = $obj->date_lettering;
1017 $line->fk_user_author = $obj->fk_user_author;
1018 $line->import_key = $obj->import_key;
1019 $line->code_journal = $obj->code_journal;
1020 $line->journal_label = $obj->journal_label;
1021 $line->piece_num = $obj->piece_num;
1022 $line->date_creation = $this->db->jdate($obj->date_creation);
1023 $line->date_export = $this->db->jdate($obj->date_export);
1024 $line->date_validation = $this->db->jdate($obj->date_validation);
1025 // Due date
1026 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1027 $line->import_key = $obj->import_key;
1028
1029 $this->lines[] = $line;
1030
1031 $i++;
1032 }
1033 }
1034 $this->db->free($resql);
1035
1036 return $num;
1037 } else {
1038 $this->errors[] = 'Error '.$this->db->lasterror();
1039 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1040
1041 return -1;
1042 }
1043 }
1044
1057 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $showAlreadyExportMovements = 1)
1058 {
1059 global $conf;
1060
1061 dol_syslog(__METHOD__, LOG_DEBUG);
1062
1063 $sql = 'SELECT';
1064 $sql .= ' t.rowid,';
1065 $sql .= " t.doc_date,";
1066 $sql .= " t.doc_type,";
1067 $sql .= " t.doc_ref,";
1068 $sql .= " t.fk_doc,";
1069 $sql .= " t.fk_docdet,";
1070 $sql .= " t.thirdparty_code,";
1071 $sql .= " t.subledger_account,";
1072 $sql .= " t.subledger_label,";
1073 $sql .= " t.numero_compte,";
1074 $sql .= " t.label_compte,";
1075 $sql .= " t.label_operation,";
1076 $sql .= " t.debit,";
1077 $sql .= " t.credit,";
1078 $sql .= " t.lettering_code,";
1079 $sql .= " t.date_lettering,";
1080 $sql .= " t.montant as amount,";
1081 $sql .= " t.sens,";
1082 $sql .= " t.fk_user_author,";
1083 $sql .= " t.import_key,";
1084 $sql .= " t.code_journal,";
1085 $sql .= " t.journal_label,";
1086 $sql .= " t.piece_num,";
1087 $sql .= " t.date_creation,";
1088 $sql .= " t.date_lim_reglement,";
1089 $sql .= " t.tms as date_modification,";
1090 $sql .= " t.date_export,";
1091 $sql .= " t.date_validated as date_validation";
1092 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1093
1094 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1095 if ($showAlreadyExportMovements == 0) {
1096 $sql .= " AND t.date_export IS NULL";
1097 }
1098
1099 // Manage filter
1100 if (is_array($filter)) { // deprecated, use $filter = USF syntax
1101 dol_syslog("You are using a deprecated use of fetchAll. filter parameter mus be an USF string now.", LOG_WARNING);
1102 $sqlwhere = array();
1103 if (count($filter) > 0) {
1104 foreach ($filter as $key => $value) {
1105 if ($key == 't.doc_date') {
1106 $sqlwhere[] = $this->db->sanitize($key).' = \''.$this->db->idate($value).'\'';
1107 } elseif ($key == 't.doc_date>=') {
1108 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
1109 } elseif ($key == 't.doc_date<=') {
1110 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
1111 } elseif ($key == 't.doc_date>') {
1112 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
1113 } elseif ($key == 't.doc_date<') {
1114 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
1115 } elseif ($key == 't.numero_compte>=') {
1116 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1117 } elseif ($key == 't.numero_compte<=') {
1118 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1119 } elseif ($key == 't.subledger_account>=') {
1120 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1121 } elseif ($key == 't.subledger_account<=') {
1122 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1123 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1124 $sqlwhere[] = $this->db->sanitize($key).' = '.((int) $value);
1125 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1126 $sqlwhere[] = $this->db->sanitize($key).' LIKE \''.$this->db->escape($value).'%\'';
1127 } elseif ($key == 't.date_creation>=') {
1128 $sqlwhere[] = 't.date_creation >= \''.$this->db->idate($value).'\'';
1129 } elseif ($key == 't.date_creation<=') {
1130 $sqlwhere[] = 't.date_creation <= \''.$this->db->idate($value).'\'';
1131 } elseif ($key == 't.tms>=') {
1132 $sqlwhere[] = 't.tms >= \''.$this->db->idate($value).'\'';
1133 } elseif ($key == 't.tms<=') {
1134 $sqlwhere[] = 't.tms <= \''.$this->db->idate($value).'\'';
1135 } elseif ($key == 't.date_export>=') {
1136 $sqlwhere[] = 't.date_export >= \''.$this->db->idate($value).'\'';
1137 } elseif ($key == 't.date_export<=') {
1138 $sqlwhere[] = 't.date_export <= \''.$this->db->idate($value).'\'';
1139 } elseif ($key == 't.date_validated>=') {
1140 $sqlwhere[] = 't.date_validated >= \''.$this->db->idate($value).'\'';
1141 } elseif ($key == 't.date_validated<=') {
1142 $sqlwhere[] = 't.date_validated <= \''.$this->db->idate($value).'\'';
1143 } elseif ($key == 't.credit' || $key == 't.debit') {
1144 $sqlwhere[] = natural_search($key, $value, 1, 1);
1145 } elseif ($key == 't.code_journal' && !empty($value)) {
1146 if (is_array($value)) {
1147 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1148 } else {
1149 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1150 }
1151 } else {
1152 $sqlwhere[] = natural_search($key, $value, 0, 1);
1153 }
1154 }
1155 }
1156 if (count($sqlwhere) > 0) {
1157 $sql .= ' AND '.implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1158 }
1159
1160 $filter = '';
1161 }
1162
1163 // Manage filter
1164 $errormessage = '';
1165 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1166 if ($errormessage) {
1167 $this->errors[] = $errormessage;
1168 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1169 return -1;
1170 }
1171
1172 if (!empty($sortfield)) {
1173 $sql .= $this->db->order($sortfield, $sortorder);
1174 }
1175 if (!empty($limit)) {
1176 $sql .= $this->db->plimit($limit + 1, $offset);
1177 }
1178 $this->lines = array();
1179
1180 $resql = $this->db->query($sql);
1181 if ($resql) {
1182 $num = $this->db->num_rows($resql);
1183
1184 $i = 0;
1185 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1186 $line = new BookKeepingLine($this->db);
1187
1188 $line->id = $obj->rowid;
1189
1190 $line->doc_date = $this->db->jdate($obj->doc_date);
1191 $line->doc_type = $obj->doc_type;
1192 $line->doc_ref = $obj->doc_ref;
1193 $line->fk_doc = $obj->fk_doc;
1194 $line->fk_docdet = $obj->fk_docdet;
1195 $line->thirdparty_code = $obj->thirdparty_code;
1196 $line->subledger_account = $obj->subledger_account;
1197 $line->subledger_label = $obj->subledger_label;
1198 $line->numero_compte = $obj->numero_compte;
1199 $line->label_compte = $obj->label_compte;
1200 $line->label_operation = $obj->label_operation;
1201 $line->debit = $obj->debit;
1202 $line->credit = $obj->credit;
1203 $line->montant = $obj->amount; // deprecated
1204 $line->amount = $obj->amount;
1205 $line->sens = $obj->sens;
1206 $line->lettering_code = $obj->lettering_code;
1207 $line->date_lettering = $obj->date_lettering;
1208 $line->fk_user_author = $obj->fk_user_author;
1209 $line->import_key = $obj->import_key;
1210 $line->code_journal = $obj->code_journal;
1211 $line->journal_label = $obj->journal_label;
1212 $line->piece_num = $obj->piece_num;
1213 $line->date_creation = $this->db->jdate($obj->date_creation);
1214 $line->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1215 $line->date_modification = $this->db->jdate($obj->date_modification);
1216 $line->date_export = $this->db->jdate($obj->date_export);
1217 $line->date_validation = $this->db->jdate($obj->date_validation);
1218
1219 $this->lines[] = $line;
1220
1221 $i++;
1222 }
1223 $this->db->free($resql);
1224
1225 return $num;
1226 } else {
1227 $this->errors[] = 'Error '.$this->db->lasterror();
1228 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1229 return -1;
1230 }
1231 }
1232
1245 public function fetchAllBalance($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $option = 0)
1246 {
1247 global $conf;
1248
1249 $this->lines = array();
1250
1251 dol_syslog(__METHOD__, LOG_DEBUG);
1252
1253 $sql = 'SELECT';
1254 $sql .= " t.numero_compte,";
1255 if (!empty($option)) {
1256 $sql .= " t.subledger_account,";
1257 $sql .= " t.subledger_label,";
1258 }
1259 $sql .= " SUM(t.debit) as debit,";
1260 $sql .= " SUM(t.credit) as credit";
1261 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' as t';
1262 $sql .= ' WHERE entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
1263
1264 // Manage filter
1265 if (is_array($filter)) {
1266 $sqlwhere = array();
1267 if (count($filter) > 0) {
1268 foreach ($filter as $key => $value) {
1269 if ($key == 't.doc_date') {
1270 $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate($value)."'";
1271 } elseif ($key == 't.doc_date>=') {
1272 $sqlwhere[] = "t.doc_date >= '".$this->db->idate($value)."'";
1273 } elseif ($key == 't.doc_date<=') {
1274 $sqlwhere[] = "t.doc_date <= '".$this->db->idate($value)."'";
1275 } elseif ($key == 't.doc_date>') {
1276 $sqlwhere[] = "t.doc_date > '".$this->db->idate($value)."'";
1277 } elseif ($key == 't.doc_date<') {
1278 $sqlwhere[] = "t.doc_date < '".$this->db->idate($value)."'";
1279 } elseif ($key == 't.numero_compte>=') {
1280 $sqlwhere[] = "t.numero_compte >= '".$this->db->escape($value)."'";
1281 } elseif ($key == 't.numero_compte<=') {
1282 $sqlwhere[] = "t.numero_compte <= '".$this->db->escape($value)."'";
1283 } elseif ($key == 't.subledger_account>=') {
1284 $sqlwhere[] = "t.subledger_account >= '".$this->db->escape($value)."'";
1285 } elseif ($key == 't.subledger_account<=') {
1286 $sqlwhere[] = "t.subledger_account <= '".$this->db->escape($value)."'";
1287 } elseif ($key == 't.fk_doc' || $key == 't.fk_docdet' || $key == 't.piece_num') {
1288 $sqlwhere[] = $this->db->sanitize($key)." = ".((int) $value);
1289 } elseif ($key == 't.subledger_account' || $key == 't.numero_compte') {
1290 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1291 } elseif ($key == 't.subledger_label') {
1292 $sqlwhere[] = $this->db->sanitize($key)." LIKE '".$this->db->escape($value)."%'";
1293 } elseif ($key == 't.code_journal' && !empty($value)) {
1294 if (is_array($value)) {
1295 $sqlwhere[] = natural_search("t.code_journal", implode(',', $value), 3, 1);
1296 } else {
1297 $sqlwhere[] = natural_search("t.code_journal", $value, 3, 1);
1298 }
1299 } elseif ($key == 't.reconciled_option') {
1300 $sqlwhere[] = 't.lettering_code IS NULL';
1301 } else {
1302 $sqlwhere[] = $this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
1303 }
1304 }
1305 }
1306 if (count($sqlwhere) > 0) {
1307 $sql .= " AND ".implode(" ".$this->db->sanitize($filtermode)." ", $sqlwhere);
1308 }
1309
1310 $filter = '';
1311 }
1312
1313 // Manage filter
1314 $errormessage = '';
1315 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1316 if ($errormessage) {
1317 $this->errors[] = $errormessage;
1318 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1319 return -1;
1320 }
1321
1322 if (!empty($option)) {
1323 $sql .= " AND t.subledger_account IS NOT NULL";
1324 $sql .= " AND t.subledger_account <> ''";
1325 $sql .= " GROUP BY t.numero_compte, t.subledger_account, t.subledger_label";
1326 $sortfield = 't.subledger_account'.($sortfield ? ','.$sortfield : '');
1327 $sortorder = 'ASC'.($sortfield ? ','.$sortfield : '');
1328 } else {
1329 $sql .= ' GROUP BY t.numero_compte';
1330 $sortfield = 't.numero_compte'.($sortfield ? ','.$sortfield : '');
1331 $sortorder = 'ASC'.($sortorder ? ','.$sortorder : '');
1332 }
1333
1334 if (!empty($sortfield)) {
1335 $sql .= $this->db->order($sortfield, $sortorder);
1336 }
1337 if (!empty($limit)) {
1338 $sql .= $this->db->plimit($limit + 1, $offset);
1339 }
1340
1341 //print $sql;
1342 $resql = $this->db->query($sql);
1343
1344 if ($resql) {
1345 $num = $this->db->num_rows($resql);
1346
1347 $i = 0;
1348 while (($obj = $this->db->fetch_object($resql)) && (empty($limit) || $i < min($limit, $num))) {
1349 $line = new BookKeepingLine($this->db);
1350
1351 $line->numero_compte = $obj->numero_compte;
1352 //$line->label_compte = $obj->label_compte;
1353 if (!empty($option)) {
1354 $line->subledger_account = $obj->subledger_account;
1355 $line->subledger_label = $obj->subledger_label;
1356 }
1357 $line->debit = $obj->debit;
1358 $line->credit = $obj->credit;
1359
1360 $this->lines[] = $line;
1361
1362 $i++;
1363 }
1364 $this->db->free($resql);
1365
1366 return $num;
1367 } else {
1368 $this->errors[] = 'Error '.$this->db->lasterror();
1369 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1370
1371 return -1;
1372 }
1373 }
1374
1383 public function update(User $user, $notrigger = 0, $mode = '')
1384 {
1385 global $langs;
1386 $error = 0;
1387
1388 dol_syslog(__METHOD__, LOG_DEBUG);
1389
1390 // Clean parameters
1391 if (isset($this->doc_type)) {
1392 $this->doc_type = trim($this->doc_type);
1393 }
1394 if (isset($this->doc_ref)) {
1395 $this->doc_ref = trim($this->doc_ref);
1396 }
1397 if (isset($this->fk_doc)) {
1398 $this->fk_doc = (int) $this->fk_doc;
1399 }
1400 if (isset($this->fk_docdet)) {
1401 $this->fk_docdet = (int) $this->fk_docdet;
1402 }
1403 if (isset($this->thirdparty_code)) {
1404 $this->thirdparty_code = trim($this->thirdparty_code);
1405 }
1406 if (isset($this->subledger_account)) {
1407 $this->subledger_account = trim($this->subledger_account);
1408 }
1409 if (isset($this->subledger_label)) {
1410 $this->subledger_label = trim($this->subledger_label);
1411 }
1412 if (isset($this->numero_compte)) {
1413 $this->numero_compte = trim($this->numero_compte);
1414 }
1415 if (isset($this->label_compte)) {
1416 $this->label_compte = trim($this->label_compte);
1417 }
1418 if (isset($this->label_operation)) {
1419 $this->label_operation = trim($this->label_operation);
1420 }
1421 if (isset($this->sens)) {
1422 $this->sens = trim($this->sens);
1423 }
1424 if (isset($this->import_key)) {
1425 $this->import_key = trim($this->import_key);
1426 }
1427 if (isset($this->code_journal)) {
1428 $this->code_journal = trim($this->code_journal);
1429 }
1430 if (isset($this->journal_label)) {
1431 $this->journal_label = trim($this->journal_label);
1432 }
1433 if (isset($this->piece_num)) {
1434 $this->piece_num = (int) $this->piece_num;
1435 }
1436
1437 $result = $this->canModifyBookkeeping($this->id, $mode);
1438 if ($result < 0) {
1439 return -1;
1440 } elseif ($result == 0) {
1441 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1442 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1443 } else {
1444 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1445 }
1446 return -1;
1447 }
1448
1449 $this->debit = (float) price2num($this->debit, 'MT');
1450 $this->credit = (float) price2num($this->credit, 'MT');
1451 $this->montant = (float) price2num($this->montant, 'MT');
1452
1453 // Check parameters
1454 // Put here code to add a control on parameters values
1455
1456 // Update request
1457 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.$mode.' SET';
1458 $sql .= ' doc_date = '.(isDolTms($this->doc_date) ? "'".$this->db->idate($this->doc_date)."'" : 'null').',';
1459 $sql .= ' doc_type = '.(isset($this->doc_type) ? "'".$this->db->escape($this->doc_type)."'" : "null").',';
1460 $sql .= ' doc_ref = '.(isset($this->doc_ref) ? "'".$this->db->escape($this->doc_ref)."'" : "null").',';
1461 $sql .= ' fk_doc = '.(isset($this->fk_doc) ? $this->fk_doc : "null").',';
1462 $sql .= ' fk_docdet = '.(isset($this->fk_docdet) ? $this->fk_docdet : "null").',';
1463 $sql .= ' thirdparty_code = '.(isset($this->thirdparty_code) ? "'".$this->db->escape($this->thirdparty_code)."'" : "null").',';
1464 $sql .= ' subledger_account = '.(isset($this->subledger_account) ? "'".$this->db->escape($this->subledger_account)."'" : "null").',';
1465 $sql .= ' subledger_label = '.(isset($this->subledger_label) ? "'".$this->db->escape($this->subledger_label)."'" : "null").',';
1466 $sql .= ' numero_compte = '.(isset($this->numero_compte) ? "'".$this->db->escape($this->numero_compte)."'" : "null").',';
1467 $sql .= ' label_compte = '.(isset($this->label_compte) ? "'".$this->db->escape($this->label_compte)."'" : "null").',';
1468 $sql .= ' label_operation = '.(isset($this->label_operation) ? "'".$this->db->escape($this->label_operation)."'" : "null").',';
1469 $sql .= ' debit = '.(isset($this->debit) ? $this->debit : "null").',';
1470 $sql .= ' credit = '.(isset($this->credit) ? $this->credit : "null").',';
1471 $sql .= ' montant = '.(isset($this->montant) ? $this->montant : "null").',';
1472 $sql .= ' sens = '.(isset($this->sens) ? "'".$this->db->escape($this->sens)."'" : "null").',';
1473 $sql .= ' fk_user_author = '.(isset($this->fk_user_author) ? $this->fk_user_author : "null").',';
1474 $sql .= ' import_key = '.(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null").',';
1475 $sql .= ' code_journal = '.(isset($this->code_journal) ? "'".$this->db->escape($this->code_journal)."'" : "null").',';
1476 $sql .= ' journal_label = '.(isset($this->journal_label) ? "'".$this->db->escape($this->journal_label)."'" : "null").',';
1477 $sql .= ' piece_num = '.(isset($this->piece_num) ? $this->piece_num : "null");
1478 $sql .= ' WHERE rowid='.((int) $this->id);
1479
1480 $this->db->begin();
1481
1482 $resql = $this->db->query($sql);
1483 if (!$resql) {
1484 $error++;
1485 $this->errors[] = 'Error '.$this->db->lasterror();
1486 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1487 }
1488
1489 // Call triggers
1490 if (! $error && ! $notrigger) {
1491 $result = $this->call_trigger('BOOKKEEPING_MODIFY', $user);
1492 if ($result < 0) {
1493 $error++;
1494 }
1495 }
1496
1497 // Commit or rollback
1498 if ($error) {
1499 $this->db->rollback();
1500
1501 return -1 * $error;
1502 } else {
1503 $this->db->commit();
1504
1505 return 1;
1506 }
1507 }
1508
1518 public function updateByMvt($piece_num = '', $field = '', $value = '', $mode = '')
1519 {
1520 $error = 0;
1521
1522 $sql_filter = $this->getCanModifyBookkeepingSQL();
1523 if (!isset($sql_filter)) {
1524 return -1;
1525 }
1526
1527 $this->db->begin();
1528
1529 $sql = "UPDATE ".$this->db->prefix().$this->table_element.$mode;
1530 $sql .= " SET ".$this->db->sanitize($field)." = ".(is_numeric($value) ? ((float) $value) : "'".$this->db->escape($value)."'");
1531 $sql .= " WHERE piece_num = ".((int) $piece_num);
1532 $sql .= $sql_filter;
1533
1534 $resql = $this->db->query($sql);
1535
1536 if (!$resql) {
1537 $error++;
1538 $this->errors[] = 'Error '.$this->db->lasterror();
1539 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1540 }
1541 if ($error) {
1542 $this->db->rollback();
1543
1544 return -1 * $error;
1545 } else {
1546 $this->db->commit();
1547
1548 return 1;
1549 }
1550 }
1551
1560 public function delete(User $user, $notrigger = 0, $mode = '')
1561 {
1562 global $langs;
1563
1564 dol_syslog(__METHOD__, LOG_DEBUG);
1565
1566 $result = $this->canModifyBookkeeping($this->id, $mode);
1567 if ($result < 0) {
1568 return -1;
1569 } elseif ($result == 0) {
1570 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
1571 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateIsOnAClosedFiscalPeriod');
1572 } else {
1573 $this->errors[] = $langs->trans('ErrorBookkeepingDocDateNotOnActiveFiscalPeriod');
1574 }
1575 return -1;
1576 }
1577
1578 $error = 0;
1579
1580 $this->db->begin();
1581
1582 // Call triggers
1583 if (! $error && ! $notrigger) {
1584 $result = $this->call_trigger('BOOKKEEPING_DELETE', $user);
1585 if ($result < 0) {
1586 $error++;
1587 }
1588 }
1589
1590 if (!$error) {
1591 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.$mode;
1592 $sql .= ' WHERE rowid='.((int) $this->id);
1593
1594 $resql = $this->db->query($sql);
1595 if (!$resql) {
1596 $error++;
1597 $this->errors[] = 'Error '.$this->db->lasterror();
1598 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1599 }
1600 }
1601
1602 // Commit or rollback
1603 if ($error) {
1604 $this->db->rollback();
1605
1606 return -1 * $error;
1607 } else {
1608 $this->db->commit();
1609
1610 return 1;
1611 }
1612 }
1613
1621 public function deleteByImportkey($importkey, $mode = '')
1622 {
1623 $this->db->begin();
1624
1625 $sql_filter = $this->getCanModifyBookkeepingSQL();
1626 if (!isset($sql_filter)) {
1627 return -1;
1628 }
1629
1630 // first check if line not yet in bookkeeping
1631 $sql = "DELETE";
1632 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1633 $sql .= " WHERE import_key = '".$this->db->escape($importkey)."'";
1634 $sql .= $sql_filter;
1635
1636 $resql = $this->db->query($sql);
1637
1638 if (!$resql) {
1639 $this->errors[] = "Error ".$this->db->lasterror();
1640 dol_syslog(get_class($this)."::delete Error ".$this->db->lasterror(), LOG_ERR);
1641 $this->db->rollback();
1642 return -1;
1643 }
1644
1645 $this->db->commit();
1646 return 1;
1647 }
1648
1658 public function deleteByYearAndJournal($delyear = 0, $journal = '', $mode = '', $delmonth = 0)
1659 {
1660 global $conf, $langs;
1661
1662 if (empty($delyear) && empty($journal)) {
1663 $this->error = 'ErrorOneFieldRequired';
1664 return -1;
1665 }
1666 if (!empty($delmonth) && empty($delyear)) {
1667 $this->error = 'YearRequiredIfMonthDefined';
1668 return -2;
1669 }
1670
1671 $sql_filter = $this->getCanModifyBookkeepingSQL();
1672 if (!isset($sql_filter)) {
1673 return -1;
1674 }
1675
1676 $this->db->begin();
1677
1678 // Delete record in bookkeeping
1679 $sql = "DELETE";
1680 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1681 $sql .= " WHERE 1 = 1";
1682 $sql .= dolSqlDateFilter('doc_date', 0, $delmonth, $delyear);
1683 if (!empty($journal)) {
1684 $sql .= " AND code_journal = '".$this->db->escape($journal)."'";
1685 }
1686 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1687 // Exclusion of validated entries at the time of deletion
1688 $sql .= " AND date_validated IS NULL";
1689 $sql .= $sql_filter;
1690
1691 // TODO: In a future we must forbid deletion if record is inside a closed fiscal period.
1692
1693 $resql = $this->db->query($sql);
1694
1695 if (!$resql) {
1696 $this->errors[] = "Error ".$this->db->lasterror();
1697 foreach ($this->errors as $errmsg) {
1698 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1699 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1700 }
1701 $this->db->rollback();
1702 return -1;
1703 }
1704
1705 $this->db->commit();
1706 return 1;
1707 }
1708
1716 public function deleteMvtNum($piecenum, $mode = '')
1717 {
1718 global $conf;
1719
1720 $sql_filter = $this->getCanModifyBookkeepingSQL();
1721 if (!isset($sql_filter)) {
1722 return -1;
1723 }
1724
1725 $nbprocessed = 0;
1726
1727 $this->db->begin();
1728
1729 // first check if line not yet in bookkeeping
1730 $sql = "DELETE";
1731 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1732 $sql .= " WHERE piece_num = ".(int) $piecenum;
1733 $sql .= " AND date_validated IS NULL"; // For security, exclusion of validated entries at the time of deletion
1734 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1735 $sql .= $sql_filter;
1736
1737 $resql = $this->db->query($sql);
1738
1739 if (!$resql) {
1740 $this->errors[] = "Error ".$this->db->lasterror();
1741 foreach ($this->errors as $errmsg) {
1742 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1743 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1744 }
1745 $this->db->rollback();
1746 return -1;
1747 } else {
1748 $nbprocessed = $this->db->affected_rows($resql);
1749 }
1750
1751 $this->db->commit();
1752
1753 return $nbprocessed;
1754 }
1755
1763 public function createFromClone(User $user, $fromid)
1764 {
1765 dol_syslog(__METHOD__, LOG_DEBUG);
1766
1767 $error = 0;
1768 $object = new BookKeeping($this->db);
1769
1770 $this->db->begin();
1771
1772 // Load source object
1773 $object->fetch($fromid);
1774 // Reset object
1775 $object->id = 0;
1776
1777 // Clear fields
1778 // ...
1779
1780 // Create clone
1781 $object->context['createfromclone'] = 'createfromclone';
1782 $result = $object->create($user);
1783
1784 // Other options
1785 if ($result < 0) {
1786 $error++;
1787 $this->errors = $object->errors;
1788 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1789 }
1790
1791 unset($object->context['createfromclone']);
1792
1793 // End
1794 if (!$error) {
1795 $this->db->commit();
1796
1797 return $object->id;
1798 } else {
1799 $this->db->rollback();
1800
1801 return -1;
1802 }
1803 }
1804
1811 public function initAsSpecimen()
1812 {
1813 global $user;
1814
1815 $now = dol_now();
1816
1817 $this->id = 0;
1818 $this->doc_date = $now;
1819 $this->doc_type = '';
1820 $this->doc_ref = '';
1821 $this->fk_doc = 0;
1822 $this->fk_docdet = 0;
1823 $this->thirdparty_code = 'CU001';
1824 $this->subledger_account = '41100001';
1825 $this->subledger_label = 'My customer company';
1826 $this->numero_compte = '411';
1827 $this->label_compte = 'Customer';
1828 $this->label_operation = 'Sales of pea';
1829 $this->debit = 99.9;
1830 $this->credit = 0.0;
1831 $this->amount = 0.0;
1832 $this->sens = 'D';
1833 $this->fk_user_author = $user->id;
1834 $this->import_key = '20201027';
1835 $this->code_journal = 'VT';
1836 $this->journal_label = 'Journal de vente';
1837 $this->piece_num = 1234;
1838 $this->date_creation = $now;
1839
1840 return 1;
1841 }
1842
1850 public function fetchPerMvt($piecenum, $mode = '')
1851 {
1852 global $conf;
1853
1854 $sql = "SELECT piece_num, doc_date, code_journal, journal_label, doc_ref, doc_type,";
1855 $sql .= " date_creation, tms as date_modification, date_validated as date_validation, date_lim_reglement, import_key";
1856 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1857 if ($mode != "_tmp") {
1858 $sql .= ", date_export";
1859 }
1860 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1861 $sql .= " WHERE piece_num = ".((int) $piecenum);
1862 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1863
1864 dol_syslog(__METHOD__, LOG_DEBUG);
1865 $result = $this->db->query($sql);
1866 if ($result) {
1867 $obj = $this->db->fetch_object($result);
1868
1869 $this->piece_num = $obj->piece_num;
1870 $this->code_journal = $obj->code_journal;
1871 $this->journal_label = $obj->journal_label;
1872 $this->doc_date = $this->db->jdate($obj->doc_date);
1873 $this->doc_ref = $obj->doc_ref;
1874 $this->doc_type = $obj->doc_type;
1875 $this->date_creation = $this->db->jdate($obj->date_creation);
1876 $this->date_modification = $this->db->jdate($obj->date_modification);
1877 if ($mode != "_tmp") {
1878 $this->date_export = $this->db->jdate($obj->date_export);
1879 }
1880 $this->date_validation = $this->db->jdate($obj->date_validation);
1881 $this->date_lim_reglement = $this->db->jdate($obj->date_lim_reglement);
1882 $this->import_key = $obj->import_key;
1883 } else {
1884 $this->error = "Error ".$this->db->lasterror();
1885 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1886 return -1;
1887 }
1888
1889 return 1;
1890 }
1891
1898 public function getNextNumMvt($mode = '')
1899 {
1900 global $conf;
1901
1902 $sql = "SELECT MAX(piece_num)+1 as max FROM ".$this->db->prefix().$this->table_element.$mode;
1903 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1904
1905 dol_syslog(get_class($this)."::getNextNumMvt", LOG_DEBUG);
1906
1907 $result = $this->db->query($sql);
1908
1909 if ($result) {
1910 $obj = $this->db->fetch_object($result);
1911 if ($obj) {
1912 $result = $obj->max;
1913 }
1914 if (empty($result)) {
1915 $result = 1;
1916 }
1917 return $result;
1918 } else {
1919 $this->error = "Error ".$this->db->lasterror();
1920 dol_syslog(get_class($this)."::getNextNumMvt ".$this->error, LOG_ERR);
1921 return -1;
1922 }
1923 }
1924
1932 public function fetchAllPerMvt($piecenum, $mode = '')
1933 {
1934 global $conf;
1935
1936 $sql = "SELECT rowid, doc_date, doc_type,";
1937 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
1938 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
1939 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, journal_label, piece_num,";
1940 $sql .= " date_creation, tms as date_modification, date_validated as date_validation";
1941 // In llx_accounting_bookkeeping_tmp, field date_export doesn't exist
1942 if ($mode != "_tmp") {
1943 $sql .= ", date_export";
1944 }
1945 $sql .= " FROM ".$this->db->prefix().$this->table_element.$mode;
1946 $sql .= " WHERE piece_num = ".((int) $piecenum);
1947 $sql .= " AND entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
1948
1949 dol_syslog(__METHOD__, LOG_DEBUG);
1950 $result = $this->db->query($sql);
1951 if ($result) {
1952 while ($obj = $this->db->fetch_object($result)) {
1953 $line = new BookKeepingLine($this->db);
1954
1955 $line->id = $obj->rowid;
1956
1957 $line->doc_date = $this->db->jdate($obj->doc_date);
1958 $line->doc_type = $obj->doc_type;
1959 $line->doc_ref = $obj->doc_ref;
1960 $line->fk_doc = $obj->fk_doc;
1961 $line->fk_docdet = $obj->fk_docdet;
1962 $line->thirdparty_code = $obj->thirdparty_code;
1963 $line->subledger_account = $obj->subledger_account;
1964 $line->subledger_label = $obj->subledger_label;
1965 $line->numero_compte = $obj->numero_compte;
1966 $line->label_compte = $obj->label_compte;
1967 $line->label_operation = $obj->label_operation;
1968 $line->debit = $obj->debit;
1969 $line->credit = $obj->credit;
1970 $line->montant = $obj->amount;
1971 $line->amount = $obj->amount;
1972 $line->sens = $obj->sens;
1973 $line->code_journal = $obj->code_journal;
1974 $line->journal_label = $obj->journal_label;
1975 $line->piece_num = $obj->piece_num;
1976 $line->date_creation = $obj->date_creation;
1977 $line->date_modification = $obj->date_modification;
1978 if ($mode != "_tmp") {
1979 $line->date_export = $obj->date_export;
1980 }
1981 $line->date_validation = $obj->date_validation;
1982
1983 $this->linesmvt[] = $line;
1984 }
1985 } else {
1986 $this->error = "Error ".$this->db->lasterror();
1987 dol_syslog(__METHOD__.$this->error, LOG_ERR);
1988 return -1;
1989 }
1990
1991 return 1;
1992 }
1993
1994 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2001 public function export_bookkeeping($model = 'ebp')
2002 {
2003 // phpcs:enable
2004 global $conf;
2005
2006 $sql = "SELECT rowid, doc_date, doc_type,";
2007 $sql .= " doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,";
2008 $sql .= " numero_compte, label_compte, label_operation, debit, credit,";
2009 $sql .= " montant as amount, sens, fk_user_author, import_key, code_journal, piece_num,";
2010 $sql .= " date_validated as date_validation";
2011 $sql .= " FROM ".$this->db->prefix().$this->table_element;
2012 $sql .= " WHERE entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2013
2014 dol_syslog(get_class($this)."::export_bookkeeping", LOG_DEBUG);
2015
2016 $resql = $this->db->query($sql);
2017
2018 if ($resql) {
2019 $this->linesexport = array();
2020
2021 $num = $this->db->num_rows($resql);
2022 while ($obj = $this->db->fetch_object($resql)) {
2023 $line = new BookKeepingLine($this->db);
2024
2025 $line->id = $obj->rowid;
2026
2027 $line->doc_date = $this->db->jdate($obj->doc_date);
2028 $line->doc_type = $obj->doc_type;
2029 $line->doc_ref = $obj->doc_ref;
2030 $line->fk_doc = $obj->fk_doc;
2031 $line->fk_docdet = $obj->fk_docdet;
2032 $line->thirdparty_code = $obj->thirdparty_code;
2033 $line->subledger_account = $obj->subledger_account;
2034 $line->subledger_label = $obj->subledger_label;
2035 $line->numero_compte = $obj->numero_compte;
2036 $line->label_compte = $obj->label_compte;
2037 $line->label_operation = $obj->label_operation;
2038 $line->debit = $obj->debit;
2039 $line->credit = $obj->credit;
2040 $line->montant = $obj->amount;
2041 $line->amount = $obj->amount;
2042 $line->sens = $obj->sens;
2043 $line->code_journal = $obj->code_journal;
2044 $line->piece_num = $obj->piece_num;
2045 $line->date_validation = $obj->date_validation;
2046
2047 $this->linesexport[] = $line;
2048 }
2049 $this->db->free($resql);
2050
2051 return $num;
2052 } else {
2053 $this->error = "Error ".$this->db->lasterror();
2054 dol_syslog(get_class($this)."::export_bookkeeping ".$this->error, LOG_ERR);
2055 return -1;
2056 }
2057 }
2058
2066 public function transformTransaction($direction = 0, $piece_num = '')
2067 {
2068 global $conf;
2069
2070 $error = 0;
2071
2072 $sql_filter = $this->getCanModifyBookkeepingSQL();
2073
2074 if (!isset($sql_filter)) {
2075 return -1;
2076 }
2077
2078 $this->db->begin();
2079
2080 if ($direction == 0) {
2081 $next_piecenum = $this->getNextNumMvt();
2082 $now = dol_now();
2083
2084 if ($next_piecenum < 0) {
2085 $error++;
2086 }
2087
2088 if (!$error) {
2089 // Delete if there is an empty line
2090 $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";
2091 $resql = $this->db->query($sql);
2092 if (!$resql) {
2093 $error++;
2094 $this->errors[] = 'Error '.$this->db->lasterror();
2095 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2096 }
2097 }
2098
2099 if (!$error) {
2100 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.' (doc_date, doc_type,';
2101 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2102 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2103 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num, date_creation)';
2104 $sql .= ' SELECT doc_date, doc_type,';
2105 $sql .= ' doc_ref, fk_doc, fk_docdet, entity, thirdparty_code, subledger_account, subledger_label,';
2106 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2107 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, '.((int) $next_piecenum).", '".$this->db->idate($now)."'";
2108 $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);
2109 $sql .= $sql_filter;
2110 $resql = $this->db->query($sql);
2111 if (!$resql) {
2112 $error++;
2113 $this->errors[] = 'Error '.$this->db->lasterror();
2114 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2115 }
2116 }
2117
2118 if (!$error) {
2119 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2120 $resql = $this->db->query($sql);
2121 if (!$resql) {
2122 $error++;
2123 $this->errors[] = 'Error '.$this->db->lasterror();
2124 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2125 }
2126 }
2127 } elseif ($direction == 1) {
2128 if (!$error) {
2129 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2130 $resql = $this->db->query($sql);
2131 if (!$resql) {
2132 $error++;
2133 $this->errors[] = 'Error '.$this->db->lasterror();
2134 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2135 }
2136 }
2137
2138 if (!$error) {
2139 $sql = 'INSERT INTO '.$this->db->prefix().$this->table_element.'_tmp (doc_date, doc_type,';
2140 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2141 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2142 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num)';
2143 $sql .= ' SELECT doc_date, doc_type,';
2144 $sql .= ' doc_ref, fk_doc, fk_docdet, thirdparty_code, subledger_account, subledger_label,';
2145 $sql .= ' numero_compte, label_compte, label_operation, debit, credit,';
2146 $sql .= ' montant, sens, fk_user_author, import_key, code_journal, journal_label, piece_num';
2147 $sql .= ' FROM '.$this->db->prefix().$this->table_element.' WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2148 $sql .= $sql_filter;
2149 $resql = $this->db->query($sql);
2150 if (!$resql) {
2151 $error++;
2152 $this->errors[] = 'Error '.$this->db->lasterror();
2153 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2154 }
2155 }
2156
2157 if (!$error) {
2158 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.'_tmp WHERE piece_num = '.((int) $piece_num).' AND entity = ' .((int) $conf->entity);
2159 $sql .= $sql_filter;
2160 $resql = $this->db->query($sql);
2161 if (!$resql) {
2162 $error++;
2163 $this->errors[] = 'Error '.$this->db->lasterror();
2164 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2165 }
2166 }
2167 }
2168 if (!$error) {
2169 $this->db->commit();
2170 return 1;
2171 } else {
2172 $this->db->rollback();
2173 return -1;
2174 }
2175 /*
2176 $sql = "DELETE FROM ";
2177 $sql .= " FROM " . $this->db->prefix() . "accounting_bookkeeping as ab";
2178 $sql .= " LEFT JOIN " . $this->db->prefix() . "accounting_account as aa ON aa.account_number = ab.numero_compte";
2179 $sql .= " AND aa.active = 1";
2180 $sql .= " INNER JOIN " . $this->db->prefix() . "accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2181 $sql .= " AND asy.rowid = " . ((int) $pcgver);
2182 $sql .= " AND ab.entity IN (" . getEntity('accountancy') . ")";
2183 $sql .= " ORDER BY account_number ASC";
2184 */
2185 }
2186
2187 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2200 public function select_account($selectid, $htmlname = 'account', $showempty = 0, $event = array(), $select_in = 0, $select_out = 0, $aabase = '')
2201 {
2202 // phpcs:enable
2203 global $conf;
2204
2205 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
2206
2207 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2208
2209 $sql = "SELECT DISTINCT ab.numero_compte as account_number, aa.label as label, aa.rowid as rowid, aa.fk_pcg_version";
2210 $sql .= " FROM ".$this->db->prefix().$this->table_element." as ab";
2211 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as aa ON aa.account_number = ab.numero_compte";
2212 $sql .= " AND aa.active = 1";
2213 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2214 $sql .= " AND asy.rowid = ".((int) $pcgver);
2215 $sql .= " AND ab.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2216 $sql .= " ORDER BY account_number ASC";
2217
2218 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2219 $resql = $this->db->query($sql);
2220
2221 if (!$resql) {
2222 $this->error = "Error ".$this->db->lasterror();
2223 dol_syslog(get_class($this)."::select_account ".$this->error, LOG_ERR);
2224 return "-1";
2225 }
2226
2227 $out = ajax_combobox($htmlname, $event);
2228
2229 $options = array();
2230 $selected = null;
2231
2232 while ($obj = $this->db->fetch_object($resql)) {
2233 $label = length_accountg($obj->account_number).' - '.$obj->label;
2234
2235 $select_value_in = $obj->rowid;
2236 $select_value_out = $obj->rowid;
2237
2238 if ($select_in == 1) {
2239 $select_value_in = $obj->account_number;
2240 }
2241 if ($select_out == 1) {
2242 $select_value_out = $obj->account_number;
2243 }
2244
2245 // Remember guy's we store in database llx_facturedet the rowid of accounting_account and not the account_number
2246 // Because same account_number can be share between different accounting_system and do have the same meaning
2247 if (($selectid != '') && $selectid == $select_value_in) {
2248 $selected = $select_value_out;
2249 }
2250
2251 $options[$select_value_out] = $label;
2252 }
2253
2254 $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300');
2255 $this->db->free($resql);
2256 return $out;
2257 }
2258
2266 public function getRootAccount($account = null)
2267 {
2268 global $conf;
2269 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2270
2271 $sql = "SELECT root.rowid, root.account_number, root.label as label,";
2272 $sql .= " parent.rowid as parent_rowid, parent.account_number as parent_account_number, parent.label as parent_label";
2273 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa";
2274 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2275 $sql .= " AND asy.rowid = ".((int) $pcgver);
2276 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as parent ON aa.account_parent = parent.rowid AND parent.active = 1";
2277 $sql .= " LEFT JOIN ".$this->db->prefix()."accounting_account as root ON parent.account_parent = root.rowid AND root.active = 1";
2278 $sql .= " WHERE aa.account_number = '".$this->db->escape($account)."'";
2279 $sql .= " AND aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2280
2281 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2282 $resql = $this->db->query($sql);
2283 if ($resql) {
2284 $obj = '';
2285 if ($this->db->num_rows($resql)) {
2286 $obj = $this->db->fetch_object($resql);
2287 }
2288
2289 $result = array('id' => $obj->rowid, 'account_number' => $obj->account_number, 'label' => $obj->label);
2290 return $result;
2291 } else {
2292 $this->error = "Error ".$this->db->lasterror();
2293 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2294
2295 return -1;
2296 }
2297 }
2298
2299 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2306 public function get_compte_desc($account = null)
2307 {
2308 // phpcs:enable
2309 global $conf;
2310
2311 $pcgver = getDolGlobalInt('CHARTOFACCOUNTS');
2312 $sql = "SELECT aa.account_number, aa.label, aa.rowid, aa.fk_pcg_version, cat.label as category";
2313 $sql .= " FROM ".$this->db->prefix()."accounting_account as aa ";
2314 $sql .= " INNER JOIN ".$this->db->prefix()."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version";
2315 $sql .= " AND aa.account_number = '".$this->db->escape($account)."'";
2316 $sql .= " AND asy.rowid = ".((int) $pcgver);
2317 $sql .= " AND aa.active = 1";
2318 $sql .= " LEFT JOIN ".$this->db->prefix()."c_accounting_category as cat ON aa.fk_accounting_category = cat.rowid";
2319 $sql .= " WHERE aa.entity = " . ((int) $conf->entity); // Do not use getEntity for accounting features
2320
2321 dol_syslog(get_class($this)."::select_account", LOG_DEBUG);
2322 $resql = $this->db->query($sql);
2323 if ($resql) {
2324 $obj = '';
2325 if ($this->db->num_rows($resql)) {
2326 $obj = $this->db->fetch_object($resql);
2327 }
2328 if (empty($obj->category)) {
2329 return $obj->label;
2330 } else {
2331 return $obj->label.' ('.$obj->category.')';
2332 }
2333 } else {
2334 $this->error = "Error ".$this->db->lasterror();
2335 dol_syslog(__METHOD__." ".$this->error, LOG_ERR);
2336 return "-1";
2337 }
2338 }
2339
2347 public function getCanModifyBookkeepingSQL($alias = '', $force = false)
2348 {
2349 global $conf;
2350
2351 $alias = trim($alias);
2352 $alias = !empty($alias) && strpos($alias, '.') === false ? $alias . "." : $alias;
2353
2354 if (!isset(self::$can_modify_bookkeeping_sql_cached[$alias]) || $force) {
2355 $result = $this->loadFiscalPeriods($force, 'active');
2356 if ($result < 0) {
2357 return null;
2358 }
2359
2360 $sql_list = array();
2361 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2362 $i = 0;
2363 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2364 $sql_list[$i] = "(";
2365 $sql_list[$i] .= "'".$this->db->idate($fiscal_period['date_start']) . "' <= ".$this->db->sanitize($alias)."doc_date";
2366 if (!empty($fiscal_period['date_end'])) {
2367 $sql_list[$i] .= " AND ";
2368 $sql_list[$i] .= $this->db->sanitize($alias)."doc_date <= '" . $this->db->idate($fiscal_period['date_end'])."'";
2369 }
2370 $sql_list[$i] .= ")";
2371 $i++;
2372 }
2373 }
2374 $sqlsanitized = implode(' OR ', $sql_list);
2375 self::$can_modify_bookkeeping_sql_cached[$alias] = empty($sql_list) ? "" : " AND (".$sqlsanitized.")";
2376 }
2377
2378 return self::$can_modify_bookkeeping_sql_cached[$alias];
2379 }
2380
2388 public function canModifyBookkeeping($id, $mode = '')
2389 {
2390 global $conf;
2391
2392 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2393 $result = $this->loadFiscalPeriods(false, 'closed');
2394
2395 if ($result < 0) {
2396 return -1;
2397 }
2398
2399 $bookkeeping = new BookKeeping($this->db);
2400 $result = $bookkeeping->fetch($id, null, $mode);
2401 if ($result <= 0) {
2402 return $result;
2403 }
2404
2405 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2406 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2407 if ($fiscal_period['date_start'] <= $bookkeeping->doc_date && $bookkeeping->doc_date <= $fiscal_period['date_end']) {
2408 return 0;
2409 }
2410 }
2411 }
2412
2413 return 1;
2414 } else {
2415 $result = $this->loadFiscalPeriods(false, 'active');
2416 if ($result < 0) {
2417 return -1;
2418 }
2419
2420 $bookkeeping = new BookKeeping($this->db);
2421 $result = $bookkeeping->fetch($id, null, $mode);
2422
2423 if ($result <= 0) {
2424 return $result;
2425 }
2426 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2427 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2428 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'])) {
2429 return 1;
2430 }
2431 }
2432 }
2433
2434 return 0;
2435 }
2436 }
2437
2446 public function accountingLabelForOperation($thirdpartyname, $reference, $labelaccount)
2447 {
2448 global $conf;
2449
2450 $accountingLabelOperation = '';
2451
2452 if (!getDolGlobalString('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') || getDolGlobalString('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 0) {
2453 $truncThirdpartyName = 16;
2454 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2455 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2456 if (!empty($reference)) {
2457 $accountingLabelOperation .= ' - '. $reference;
2458 }
2459 if (!empty($labelaccount)) {
2460 $accountingLabelOperation .= ' - '. $labelaccount;
2461 }
2462 } elseif (getDolGlobalString('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 1) {
2463 $truncThirdpartyName = 32;
2464 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2465 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2466 if (!empty($reference)) {
2467 $accountingLabelOperation .= ' - '. $reference;
2468 }
2469 } elseif (getDolGlobalString('ACCOUNTING_LABEL_OPERATION_ON_TRANSFER') == 2) {
2470 $truncThirdpartyName = 64;
2471 // Avoid trunc with dot in accountancy for the compatibility with another accounting software
2472 $accountingLabelOperation = dol_trunc($thirdpartyname, $truncThirdpartyName, 'right', 'UTF-8', 1);
2473 }
2474 dol_syslog('label'.$accountingLabelOperation, LOG_ERR);
2475
2476 return $accountingLabelOperation;
2477 }
2478
2485 public function validBookkeepingDate($date)
2486 {
2487 global $conf;
2488
2489 if (getDolGlobalString('ACCOUNTANCY_FISCAL_PERIOD_MODE') == 'blockedonclosed') {
2490 $result = $this->loadFiscalPeriods(false, 'closed');
2491
2492 if ($result < 0) {
2493 return -1;
2494 }
2495
2496 if (!empty($conf->cache['closed_fiscal_period_cached']) && is_array($conf->cache['closed_fiscal_period_cached'])) {
2497 foreach ($conf->cache['closed_fiscal_period_cached'] as $fiscal_period) {
2498 if ($fiscal_period['date_start'] <= $date && $date <= $fiscal_period['date_end']) {
2499 return 0;
2500 }
2501 }
2502 }
2503
2504 return 1;
2505 } else {
2506 $result = $this->loadFiscalPeriods(false, 'active');
2507 if ($result < 0) {
2508 return -1;
2509 }
2510
2511 if (!empty($conf->cache['active_fiscal_period_cached']) && is_array($conf->cache['active_fiscal_period_cached'])) {
2512 foreach ($conf->cache['active_fiscal_period_cached'] as $fiscal_period) {
2513 if (!empty($fiscal_period['date_start']) && $fiscal_period['date_start'] <= $date && (empty($fiscal_period['date_end']) || $date <= $fiscal_period['date_end'])) {
2514 return 1;
2515 }
2516 }
2517 }
2518
2519 return 0;
2520 }
2521 }
2522
2530 public function loadFiscalPeriods($force = false, $mode = 'active')
2531 {
2532 global $conf;
2533
2534 if ($mode == 'active') {
2535 if (!isset($conf->cache['active_fiscal_period_cached']) || $force) {
2536 $sql = "SELECT date_start, date_end";
2537 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2538 $sql .= " WHERE entity = " . ((int) $conf->entity);
2539 $sql .= " AND statut = 0";
2540
2541 $resql = $this->db->query($sql);
2542 if (!$resql) {
2543 $this->errors[] = $this->db->lasterror();
2544 return -1;
2545 }
2546
2547 $list = array();
2548 while ($obj = $this->db->fetch_object($resql)) {
2549 $list[] = array(
2550 'date_start' => $this->db->jdate($obj->date_start),
2551 'date_end' => $this->db->jdate($obj->date_end),
2552 );
2553 }
2554 $conf->cache['active_fiscal_period_cached'] = $list;
2555 }
2556 }
2557 if ($mode == 'closed') {
2558 if (!isset($conf->cache['closed_fiscal_period_cached']) || $force) {
2559 $sql = "SELECT date_start, date_end";
2560 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2561 $sql .= " WHERE entity = " . ((int) $conf->entity);
2562 $sql .= " AND statut = 1";
2563
2564 $resql = $this->db->query($sql);
2565 if (!$resql) {
2566 $this->errors[] = $this->db->lasterror();
2567 return -1;
2568 }
2569
2570 $list = array();
2571 while ($obj = $this->db->fetch_object($resql)) {
2572 $list[] = array(
2573 'date_start' => $this->db->jdate($obj->date_start),
2574 'date_end' => $this->db->jdate($obj->date_end),
2575 );
2576 }
2577 $conf->cache['closed_fiscal_period_cached'] = $list;
2578 }
2579 }
2580
2581 return 1;
2582 }
2583
2590 public function getFiscalPeriods($filter = '')
2591 {
2592 global $conf;
2593 $list = array();
2594
2595 $sql = "SELECT rowid, label, date_start, date_end, statut";
2596 $sql .= " FROM " . $this->db->prefix() . "accounting_fiscalyear";
2597 $sql .= " WHERE entity = " . ((int) $conf->entity);
2598 if (!empty($filter)) {
2599 $sql .= " AND (" . $this->db->sanitize($filter, 1, 1, 1) . ')';
2600 }
2601 $sql .= $this->db->order('date_start', 'ASC');
2602
2603 $resql = $this->db->query($sql);
2604 if (!$resql) {
2605 $this->errors[] = $this->db->lasterror();
2606 return -1;
2607 }
2608
2609 while ($obj = $this->db->fetch_object($resql)) {
2610 $list[$obj->rowid] = array(
2611 'id' => (int) $obj->rowid,
2612 'label' => $obj->label,
2613 'date_start' => $this->db->jdate($obj->date_start),
2614 'date_end' => $this->db->jdate($obj->date_end),
2615 'status' => (int) $obj->statut,
2616 );
2617 }
2618
2619 return $list;
2620 }
2621
2630 public function getCountByMonthForFiscalPeriod($date_start, $date_end)
2631 {
2632 global $conf;
2633
2634 $total = 0;
2635 $list = array();
2636
2637 $sql = "SELECT YEAR(b.doc_date) as year";
2638 for ($i = 1; $i <= 12; $i++) {
2639 $sql .= ", SUM(".$this->db->ifsql("MONTH(b.doc_date) = ".((int) $i), "1", "0") . ") AS month".((int) $i);
2640 }
2641 $sql .= ", COUNT(b.rowid) as total";
2642 $sql .= " FROM " . $this->db->prefix() . $this->table_element . " as b";
2643 $sql .= " WHERE b.doc_date >= '" . $this->db->idate($date_start) . "'";
2644 $sql .= " AND b.doc_date <= '" . $this->db->idate($date_end) . "'";
2645 $sql .= " AND b.entity IN (" . getEntity('bookkeeping', 0) . ")"; // We don't share object for accountancy
2646
2647 // Get count for each month into the fiscal period
2648 if (getDolGlobalString("ACCOUNTANCY_DISABLE_CLOSURE_LINE_BY_LINE")) {
2649 // Loop on each closed period
2650 $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)";
2651 } else {
2652 // Filter on the unitary flag/date lock on each record
2653 $sql .= " AND date_validated IS NULL"; // not locked
2654 }
2655
2656 $sql .= " GROUP BY YEAR(b.doc_date)";
2657 $sql .= $this->db->order("year", 'ASC');
2658
2659 dol_syslog(__METHOD__, LOG_DEBUG);
2660 $resql = $this->db->query($sql);
2661 if (!$resql) {
2662 $this->errors[] = $this->db->lasterror();
2663 return -1;
2664 }
2665
2666 while ($obj = $this->db->fetch_object($resql)) {
2667 $total += (int) $obj->total;
2668 $year_list = array(
2669 'year' => (int) $obj->year,
2670 'count' => array(),
2671 'total' => (int) $obj->total,
2672 );
2673 for ($i = 1; $i <= 12; $i++) {
2674 $year_list['count'][$i] = (int) $obj->{'month' . $i};
2675 }
2676
2677 $list[] = $year_list;
2678 }
2679
2680 $this->db->free($resql);
2681
2682 return array(
2683 'total' => $total,
2684 'list' => $list,
2685 );
2686 }
2687
2695 public function validateMovementForFiscalPeriod($date_start, $date_end)
2696 {
2697 global $conf;
2698
2699 $now = dol_now();
2700
2701 // Specify as export : update field date_validated on selected month/year
2702 $sql = " UPDATE " . $this->db->prefix() . $this->table_element;
2703 $sql .= " SET date_validated = '" . $this->db->idate($now) . "'";
2704 $sql .= " WHERE entity = " . ((int) $conf->entity);
2705 $sql .= " AND DATE(doc_date) >= '" . $this->db->idate($date_start) . "'";
2706 $sql .= " AND DATE(doc_date) <= '" . $this->db->idate($date_end) . "'";
2707 $sql .= " AND date_validated IS NULL";
2708
2709 dol_syslog(__METHOD__, LOG_DEBUG);
2710 $resql = $this->db->query($sql);
2711 if (!$resql) {
2712 $this->errors[] = $this->db->lasterror();
2713 return -1;
2714 }
2715
2716 return 1;
2717 }
2718
2726 public function accountingResult($date_start, $date_end)
2727 {
2728 global $conf;
2729
2730 $this->db->begin();
2731
2732 $income_statement_amount = 0;
2733
2734 if (getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT')) {
2735 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2736
2737 $pcg_type_filter = array();
2738 foreach ($accounting_groups_used_for_income_statement as $item) {
2739 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2740 }
2741
2742 $sql = 'SELECT';
2743 $sql .= " t.numero_compte,";
2744 $sql .= " aa.pcg_type,";
2745 $sql .= " (SUM(t.credit) - SUM(t.debit)) as accounting_result";
2746 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
2747 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2748 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2749 $sql .= " AND aa.entity = " . ((int) $conf->entity);
2750 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM ' . $this->db->prefix() . 'accounting_system WHERE rowid = ' . ((int) getDolGlobalInt('CHARTOFACCOUNTS')) . ')';
2751 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2752 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
2753 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
2754 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2755
2756 $resql = $this->db->query($sql);
2757 if (!$resql) {
2758 $this->errors[] = 'Error ' . $this->db->lasterror();
2759 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2760 } else {
2761 while ($obj = $this->db->fetch_object($resql)) {
2762 $income_statement_amount += $obj->accounting_result;
2763 }
2764 }
2765 }
2766
2767 return (string) $income_statement_amount;
2768 }
2769
2779 public function closeFiscalPeriod($fiscal_period_id, $new_fiscal_period_id, $separate_auxiliary_account = false, $generate_bookkeeping_records = true)
2780 {
2781 global $conf, $langs, $user;
2782
2783 // Current fiscal period
2784 $fiscal_period_id = max(0, $fiscal_period_id);
2785 if (empty($fiscal_period_id)) {
2786 $langs->load('errors');
2787 $this->errors[] = $langs->trans('ErrorBadParameters');
2788 return -1;
2789 }
2790 $fiscal_period = new Fiscalyear($this->db);
2791 $result = $fiscal_period->fetch($fiscal_period_id);
2792 if ($result < 0) {
2793 $this->error = $fiscal_period->error;
2794 $this->errors = $fiscal_period->errors;
2795 return -1;
2796 } elseif (empty($fiscal_period->id)) {
2797 $langs->loadLangs(array('errors', 'compta'));
2798 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
2799 return -1;
2800 }
2801
2802 // New fiscal period
2803 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
2804 if (empty($new_fiscal_period_id)) {
2805 $langs->load('errors');
2806 $this->errors[] = $langs->trans('ErrorBadParameters');
2807 return -1;
2808 }
2809 $new_fiscal_period = new Fiscalyear($this->db);
2810 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
2811 if ($result < 0) {
2812 $this->error = $new_fiscal_period->error;
2813 $this->errors = $new_fiscal_period->errors;
2814 return -1;
2815 } elseif (empty($new_fiscal_period->id)) {
2816 $langs->loadLangs(array('errors', 'compta'));
2817 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
2818 return -1;
2819 }
2820
2821 $error = 0;
2822 $this->db->begin();
2823
2824 $fiscal_period->statut = Fiscalyear::STATUS_CLOSED;
2825 $fiscal_period->status = Fiscalyear::STATUS_CLOSED; // Actually not used
2826 $result = $fiscal_period->update($user);
2827 if ($result < 0) {
2828 $this->error = $fiscal_period->error;
2829 $this->errors = $fiscal_period->errors;
2830 $error++;
2831 }
2832
2833 if (!$error && !empty($generate_bookkeeping_records)) {
2834 $journal_id = max(0, getDolGlobalString('ACCOUNTING_CLOSURE_DEFAULT_JOURNAL'));
2835 if (empty($journal_id)) {
2836 $langs->loadLangs(array('errors', 'accountancy'));
2837 $this->errors[] = $langs->trans('ErrorBadParameters') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2838 $error++;
2839 }
2840
2841 // Fetch journal
2842 if (!$error) {
2843 $journal = new AccountingJournal($this->db);
2844 $result = $journal->fetch($journal_id);
2845 if ($result < 0) {
2846 $this->error = $journal->error;
2847 $this->errors = $journal->errors;
2848 $error++;
2849 } elseif ($result == 0) {
2850 $langs->loadLangs(array('errors', 'accountancy'));
2851 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('Codejournal') . ' (' . $langs->trans('AccountingJournalType9') . ')';
2852 $error++;
2853 }
2854 } else {
2855 $journal = null;
2856 }
2857
2858 if (!$error && is_object($journal)) {
2859 $accounting_groups_used_for_balance_sheet_account = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_BALANCE_SHEET_ACCOUNT'))), 'strlen');
2860 $accounting_groups_used_for_income_statement = array_filter(array_map('trim', explode(',', getDolGlobalString('ACCOUNTING_CLOSURE_ACCOUNTING_GROUPS_USED_FOR_INCOME_STATEMENT'))), 'strlen');
2861
2862 $pcg_type_filter = array();
2863 $tmp = array_merge($accounting_groups_used_for_balance_sheet_account, $accounting_groups_used_for_income_statement);
2864 foreach ($tmp as $item) {
2865 $pcg_type_filter[] = "'" . $this->db->escape($item) . "'";
2866 }
2867
2868 $sql = 'SELECT';
2869 $sql .= " t.numero_compte,";
2870 if ($separate_auxiliary_account) {
2871 $sql .= " NULLIF(t.subledger_account, '') as subledger_account,"; // fix db issues with Null or "" values
2872 }
2873 $sql .= " aa.pcg_type,";
2874 $sql .= " (SUM(t.credit) - SUM(t.debit)) as opening_balance";
2875 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
2876 $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'accounting_account as aa ON aa.account_number = t.numero_compte';
2877 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
2878 $sql .= " AND aa.entity = ". ((int) $conf->entity);
2879 $sql .= ' AND aa.fk_pcg_version IN (SELECT pcg_version FROM '.$this->db->prefix().'accounting_system WHERE rowid = '.((int) getDolGlobalInt('CHARTOFACCOUNTS')).')';
2880 $sql .= ' AND aa.pcg_type IN (' . $this->db->sanitize(implode(',', $pcg_type_filter), 1) . ')';
2881 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
2882 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
2883 $sql .= ' GROUP BY t.numero_compte, aa.pcg_type';
2884 if ($separate_auxiliary_account) {
2885 $sql .= " , NULLIF(t.subledger_account, '')";
2886 }
2887 $sql .= ' HAVING (SUM(t.credit) - SUM(t.debit)) != 0 '; // Exclude rows with opening_balance = 0
2888 $sql .= $this->db->order("t.numero_compte", "ASC");
2889
2890 $resql = $this->db->query($sql);
2891 if (!$resql) {
2892 $this->errors[] = 'Error ' . $this->db->lasterror();
2893 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2894
2895 $error++;
2896 } else {
2897 $now = dol_now();
2898 $income_statement_amount = 0;
2899 while ($obj = $this->db->fetch_object($resql)) {
2900 if (in_array($obj->pcg_type, $accounting_groups_used_for_income_statement)) {
2901 $income_statement_amount += $obj->opening_balance;
2902 } else {
2903 // Insert bookkeeping record for balance sheet account
2904 $mt = $obj->opening_balance;
2905
2906 $bookkeeping = new BookKeeping($this->db);
2907 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2908
2909 $bookkeeping->date_lim_reglement = '';
2910 $bookkeeping->doc_ref = $fiscal_period->label;
2911
2912 $bookkeeping->date_creation = $now;
2913 $bookkeeping->doc_type = 'closure';
2914 $bookkeeping->fk_doc = $fiscal_period->id;
2915 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2916 $bookkeeping->thirdparty_code = '';
2917
2918 if ($separate_auxiliary_account) {
2919 $bookkeeping->subledger_account = $obj->subledger_account;
2920 $sql = 'SELECT';
2921 $sql .= " subledger_label";
2922 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
2923 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
2924 $sql .= " ORDER BY doc_date DESC";
2925 $sql .= " LIMIT 1";
2926 $result = $this->db->query($sql);
2927 if (!$result) {
2928 $this->errors[] = 'Error: ' . $this->db->lasterror();
2929 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2930 $error++;
2931 }
2932 $objtmp = $this->db->fetch_object($result);
2933 $bookkeeping->subledger_label = $objtmp->subledger_label; // latest subledger label used
2934 } else {
2935 $bookkeeping->subledger_account = null;
2936 $bookkeeping->subledger_label = null;
2937 }
2938
2939 $bookkeeping->numero_compte = $obj->numero_compte;
2940 $accountingaccount = new AccountingAccount($this->db);
2941 $accountingaccount->fetch(0, $obj->numero_compte);
2942 $bookkeeping->label_compte = $accountingaccount->label; // latest account label used
2943
2944 $bookkeeping->label_operation = $new_fiscal_period->label;
2945 $bookkeeping->montant = $mt;
2946 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
2947 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
2948 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
2949 $bookkeeping->code_journal = $journal->code;
2950 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
2951 $bookkeeping->fk_user_author = $user->id;
2952 $bookkeeping->entity = $conf->entity;
2953
2954 $result = $bookkeeping->create($user);
2955 if ($result < 0) {
2956 $this->setErrorsFromObject($bookkeeping);
2957 $error++;
2958 break;
2959 }
2960 }
2961 }
2962
2963 // Insert bookkeeping record for income statement
2964 if (!$error && $income_statement_amount != 0) {
2965 $mt = $income_statement_amount;
2966 $accountingaccount = new AccountingAccount($this->db);
2967 $accountingaccount->fetch(0, getDolGlobalString($income_statement_amount < 0 ? 'ACCOUNTING_RESULT_LOSS' : 'ACCOUNTING_RESULT_PROFIT'), true);
2968
2969 $bookkeeping = new BookKeeping($this->db);
2970 $bookkeeping->doc_date = $new_fiscal_period->date_start;
2971
2972 $bookkeeping->date_lim_reglement = '';
2973 $bookkeeping->doc_ref = $fiscal_period->label;
2974
2975 $bookkeeping->date_creation = $now;
2976 $bookkeeping->doc_type = 'closure';
2977 $bookkeeping->fk_doc = $fiscal_period->id;
2978 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
2979 $bookkeeping->thirdparty_code = '';
2980
2981 if ($separate_auxiliary_account) {
2982 $bookkeeping->subledger_account = $obj->subledger_account;
2983 $sql = 'SELECT';
2984 $sql .= " subledger_label";
2985 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
2986 $sql .= " WHERE subledger_account = '" . $this->db->escape($obj->subledger_account) . "'";
2987 $sql .= " ORDER BY doc_date DESC";
2988 $sql .= " LIMIT 1";
2989 $result = $this->db->query($sql);
2990 if (!$result) {
2991 $this->errors[] = 'Error: ' . $this->db->lasterror();
2992 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
2993 $error++;
2994 }
2995 $objtmp = $this->db->fetch_object($result);
2996 $bookkeeping->subledger_label = $objtmp->subledger_label; // latest subledger label used
2997 } else {
2998 $bookkeeping->subledger_account = null;
2999 $bookkeeping->subledger_label = null;
3000 }
3001
3002 $bookkeeping->numero_compte = $accountingaccount->account_number;
3003 $bookkeeping->label_compte = $accountingaccount->label;
3004
3005 $bookkeeping->label_operation = $new_fiscal_period->label;
3006 $bookkeeping->montant = $mt;
3007 $bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
3008 $bookkeeping->debit = ($mt < 0) ? -$mt : 0;
3009 $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
3010 $bookkeeping->code_journal = $journal->code;
3011 $bookkeeping->journal_label = $langs->transnoentities($journal->label);
3012 $bookkeeping->fk_user_author = $user->id;
3013 $bookkeeping->entity = $conf->entity;
3014
3015 $result = $bookkeeping->create($user);
3016 if ($result < 0) {
3017 $this->setErrorsFromObject($bookkeeping);
3018 $error++;
3019 }
3020 }
3021 $this->db->free($resql);
3022 }
3023 }
3024 }
3025
3026 if ($error) {
3027 $this->db->rollback();
3028 return -1;
3029 } else {
3030 $this->db->commit();
3031 return 1;
3032 }
3033 }
3034
3045 public function insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
3046 {
3047 global $conf, $langs, $user;
3048
3049 // Current fiscal period
3050 $fiscal_period_id = max(0, $fiscal_period_id);
3051 if (empty($fiscal_period_id)) {
3052 $langs->load('errors');
3053 $this->errors[] = $langs->trans('ErrorBadParameters');
3054 return -1;
3055 }
3056 $fiscal_period = new Fiscalyear($this->db);
3057 $result = $fiscal_period->fetch($fiscal_period_id);
3058 if ($result < 0) {
3059 $this->error = $fiscal_period->error;
3060 $this->errors = $fiscal_period->errors;
3061 return -1;
3062 } elseif (empty($fiscal_period->id)) {
3063 $langs->loadLangs(array('errors', 'compta'));
3064 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $fiscal_period_id . ')';
3065 return -1;
3066 }
3067
3068 // New fiscal period
3069 $new_fiscal_period_id = max(0, $new_fiscal_period_id);
3070 if (empty($new_fiscal_period_id)) {
3071 $langs->load('errors');
3072 $this->errors[] = $langs->trans('ErrorBadParameters');
3073 return -1;
3074 }
3075 $new_fiscal_period = new Fiscalyear($this->db);
3076 $result = $new_fiscal_period->fetch($new_fiscal_period_id);
3077 if ($result < 0) {
3078 $this->error = $new_fiscal_period->error;
3079 $this->errors = $new_fiscal_period->errors;
3080 return -1;
3081 } elseif (empty($new_fiscal_period->id)) {
3082 $langs->loadLangs(array('errors', 'compta'));
3083 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('FiscalPeriod') . ' (' . $new_fiscal_period_id . ')';
3084 return -1;
3085 }
3086
3087 // Inventory journal
3088 $inventory_journal_id = max(0, $inventory_journal_id);
3089 if (empty($inventory_journal_id)) {
3090 $langs->load('errors');
3091 $this->errors[] = $langs->trans('ErrorBadParameters');
3092 return -1;
3093 }
3094 // Fetch journal
3095 $inventory_journal = new AccountingJournal($this->db);
3096 $result = $inventory_journal->fetch($inventory_journal_id);
3097 if ($result < 0) {
3098 $this->error = $inventory_journal->error;
3099 $this->errors = $inventory_journal->errors;
3100 return -1;
3101 } elseif ($result == 0) {
3102 $langs->loadLangs(array('errors', 'accountancy'));
3103 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('InventoryJournal');
3104 return -1;
3105 }
3106
3107 $error = 0;
3108 $this->db->begin();
3109
3110 $sql = 'SELECT t.rowid';
3111 $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t';
3112 $sql .= ' WHERE t.entity = ' . ((int) $conf->entity); // Do not use getEntity for accounting features
3113 $sql .= " AND code_journal = '" . $this->db->escape($inventory_journal->code) . "'";
3114 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($date_start) . "'";
3115 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($date_end) . "'";
3116 $sql .= " AND DATE(t.doc_date) >= '" . $this->db->idate($fiscal_period->date_start) . "'";
3117 $sql .= " AND DATE(t.doc_date) <= '" . $this->db->idate($fiscal_period->date_end) . "'";
3118
3119 $resql = $this->db->query($sql);
3120 if (!$resql) {
3121 $this->errors[] = 'Error ' . $this->db->lasterror();
3122 dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
3123
3124 $error++;
3125 } else {
3126 $now = dol_now();
3127 while ($obj = $this->db->fetch_object($resql)) {
3128 $bookkeeping = new BookKeeping($this->db);
3129 $result = $bookkeeping->fetch($obj->rowid);
3130 if ($result < 0) {
3131 $this->error = $inventory_journal->error;
3132 $this->errors = $inventory_journal->errors;
3133 $error++;
3134 break;
3135 } elseif ($result == 0) {
3136 $langs->loadLangs(array('errors', 'accountancy'));
3137 $this->errors[] = $langs->trans('ErrorRecordNotFound') . ' - ' . $langs->trans('LineId') . ': ' . $obj->rowid;
3138 $error++;
3139 break;
3140 }
3141
3142 $bookkeeping->id = 0;
3143 $bookkeeping->doc_date = $new_fiscal_period->date_start;
3144 $bookkeeping->doc_ref = $new_fiscal_period->label;
3145 $bookkeeping->date_creation = $now;
3146 $bookkeeping->doc_type = 'accounting_reversal';
3147 $bookkeeping->fk_doc = $new_fiscal_period->id;
3148 $bookkeeping->fk_docdet = 0; // Useless, can be several lines that are source of this record to add
3149
3150 $bookkeeping->montant = -$bookkeeping->montant;
3151 $bookkeeping->sens = ($bookkeeping->montant >= 0) ? 'C' : 'D';
3152 $old_debit = $bookkeeping->debit;
3153 $bookkeeping->debit = $bookkeeping->credit;
3154 $bookkeeping->credit = $old_debit;
3155
3156 $bookkeeping->fk_user_author = $user->id;
3157 $bookkeeping->entity = $conf->entity;
3158
3159 $result = $bookkeeping->create($user);
3160 if ($result < 0) {
3161 $this->error = $bookkeeping->error;
3162 $this->errors = $bookkeeping->errors;
3163 $error++;
3164 break;
3165 }
3166 }
3167 $this->db->free($resql);
3168 }
3169
3170 if ($error) {
3171 $this->db->rollback();
3172 return -1;
3173 } else {
3174 $this->db->commit();
3175 return 1;
3176 }
3177 }
3178}
3179
3184{
3188 public $id;
3189
3193 public $doc_date = null;
3197 public $doc_type;
3198
3202 public $doc_ref;
3203
3207 public $fk_doc;
3208
3212 public $fk_docdet;
3213
3217 public $thirdparty_code;
3218
3222 public $subledger_account;
3223
3227 public $subledger_label;
3228
3232 public $numero_compte;
3233
3237 public $label_compte;
3238
3242 public $label_operation;
3243
3247 public $debit;
3248
3252 public $credit;
3253
3258 public $montant;
3259
3263 public $amount;
3264
3268 public $multicurrency_amount;
3269
3273 public $multicurrency_code;
3274
3278 public $sens;
3279
3283 public $lettering_code;
3284
3288 public $date_lettering;
3289
3293 public $fk_user_author;
3294
3295
3299 public $import_key;
3300
3304 public $code_journal;
3305
3309 public $journal_label;
3313 public $piece_num;
3314
3318 public $date_export;
3319
3323 public $date_lim_reglement;
3324}
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)
accountingLabelForOperation($thirdpartyname, $reference, $labelaccount)
Generate label operation when operation is transferred into accounting.
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.
getNextNumMvt($mode='')
Return next movement number.
get_compte_desc($account=null)
Description of accounting account.
__construct(DoliDB $db)
Constructor.
createFromClone(User $user, $fromid)
Load an object from its id and create a new one in database.
deleteByImportkey($importkey, $mode='')
Delete bookkeeping by importkey.
getRootAccount($account=null)
Return id and description of a root accounting account.
transformTransaction($direction=0, $piece_num='')
Transform transaction.
insertAccountingReversal($fiscal_period_id, $inventory_journal_id, $new_fiscal_period_id, $date_start, $date_end)
Insert accounting reversal into the inventory journal of the new fiscal period.
fetchPerMvt($piecenum, $mode='')
Load an accounting document into memory from database.
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.
getFiscalPeriods($filter='')
Get list of fiscal period ordered by start date.
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
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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