dolibarr  x.y.z
lettering.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2004-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2013 Olivier Geffroy <jeff@jeffinfo.com>
4  * Copyright (C) 2013-2019 Alexandre Spangaro <aspangaro@open-dsi.fr>
5  * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
27 include_once DOL_DOCUMENT_ROOT."/accountancy/class/bookkeeping.class.php";
28 include_once DOL_DOCUMENT_ROOT."/societe/class/societe.class.php";
29 include_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php";
30 
34 class Lettering extends BookKeeping
35 {
36  public static $doc_type_infos = array(
37  'customer_invoice' => array(
38  'payment_table' => 'paiement',
39  'payment_table_fk_bank' => 'fk_bank',
40  'doc_payment_table' => 'paiement_facture',
41  'doc_payment_table_fk_payment' => 'fk_paiement',
42  'doc_payment_table_fk_doc' => 'fk_facture',
43  'linked_info' => array(
44  array(
45  'table' => 'paiement_facture',
46  'fk_doc' => 'fk_facture',
47  'fk_link' => 'fk_paiement',
48  'prefix' => 'p',
49  ),
50  array(
51  'table' => 'societe_remise_except',
52  'fk_doc' => 'fk_facture_source',
53  'fk_link' => 'fk_facture',
54  'prefix' => 'a',
55  'is_fk_link_is_also_fk_doc' => true,
56  ),
57  ),
58  ),
59  'supplier_invoice' => array(
60  'payment_table' => 'paiementfourn',
61  'payment_table_fk_bank' => 'fk_bank',
62  'doc_payment_table' => 'paiementfourn_facturefourn',
63  'doc_payment_table_fk_payment' => 'fk_paiementfourn',
64  'doc_payment_table_fk_doc' => 'fk_facturefourn',
65  'linked_info' => array(
66  array(
67  'table' => 'paiementfourn_facturefourn',
68  'fk_doc' => 'fk_facturefourn',
69  'fk_link' => 'fk_paiementfourn',
70  'prefix' => 'p',
71  ),
72  array(
73  'table' => 'societe_remise_except',
74  'fk_doc' => 'fk_invoice_supplier_source',
75  'fk_link' => 'fk_invoice_supplier',
76  'prefix' => 'a',
77  'is_fk_link_is_also_fk_doc' => true,
78  ),
79  ),
80  ),
81  );
82 
89  public function letteringThirdparty($socid)
90  {
91  global $conf;
92 
93  $error = 0;
94 
95  $object = new Societe($this->db);
96  $object->id = $socid;
97  $object->fetch($socid);
98 
99 
100  if ($object->code_compta == '411CUSTCODE') {
101  $object->code_compta = '';
102  }
103 
104  if ($object->code_compta_fournisseur == '401SUPPCODE') {
105  $object->code_compta_fournisseur = '';
106  }
107 
111  $sql = "SELECT DISTINCT bk.rowid, bk.doc_date, bk.doc_type, bk.doc_ref, bk.subledger_account, ";
112  $sql .= " bk.numero_compte , bk.label_compte, bk.debit , bk.credit, bk.montant ";
113  $sql .= " , bk.sens , bk.code_journal , bk.piece_num, bk.date_lettering, bu.url_id , bu.type ";
114  $sql .= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as bk";
115  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu ON(bk.fk_doc = bu.fk_bank AND bu.type IN ('payment', 'payment_supplier') ) ";
116  $sql .= " WHERE ( ";
117  if ($object->code_compta != "") {
118  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta)."' ";
119  }
120  if ($object->code_compta != "" && $object->code_compta_fournisseur != "") {
121  $sql .= " OR ";
122  }
123  if ($object->code_compta_fournisseur != "") {
124  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta_fournisseur)."' ";
125  }
126 
127  $sql .= " ) AND (bk.date_lettering ='' OR bk.date_lettering IS NULL) ";
128  $sql .= " AND (bk.lettering_code != '' OR bk.lettering_code IS NULL) ";
129  $sql .= ' AND bk.date_validated IS NULL ';
130  $sql .= $this->db->order('bk.doc_date', 'DESC');
131 
132  // echo $sql;
133  //
134  $resql = $this->db->query($sql);
135  if ($resql) {
136  $num = $this->db->num_rows($resql);
137 
138  while ($obj = $this->db->fetch_object($resql)) {
139  $ids = array();
140  $ids_fact = array();
141 
142  if ($obj->type == 'payment_supplier') {
143  $sql = 'SELECT DISTINCT bk.rowid, facf.ref, facf.ref_supplier, payf.fk_bank, facf.rowid as fact_id';
144  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn facf ";
145  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as payfacf ON payfacf.fk_facturefourn=facf.rowid";
146  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."paiementfourn as payf ON payfacf.fk_paiementfourn=payf.rowid";
147  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_bookkeeping as bk ON (bk.fk_doc = payf.fk_bank AND bk.code_journal='".$this->db->escape($obj->code_journal)."')";
148  $sql .= " WHERE payfacf.fk_paiementfourn = '".$this->db->escape($obj->url_id)."' ";
149  $sql .= " AND facf.entity = ".$conf->entity;
150  $sql .= " AND code_journal IN (SELECT code FROM ".MAIN_DB_PREFIX."accounting_journal WHERE nature=4 AND entity=".$conf->entity.") ";
151  $sql .= " AND ( ";
152  if ($object->code_compta != "") {
153  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta)."' ";
154  }
155  if ($object->code_compta != "" && $object->code_compta_fournisseur != "") {
156  $sql .= " OR ";
157  }
158  if ($object->code_compta_fournisseur != "") {
159  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta_fournisseur)."' ";
160  }
161  $sql .= " ) ";
162 
163  $resql2 = $this->db->query($sql);
164  if ($resql2) {
165  while ($obj2 = $this->db->fetch_object($resql2)) {
166  $ids[$obj2->rowid] = $obj2->rowid;
167  $ids_fact[] = $obj2->fact_id;
168  }
169  $this->db->free($resql2);
170  } else {
171  $this->errors[] = $this->db->lasterror;
172  return -1;
173  }
174  if (count($ids_fact)) {
175  $sql = 'SELECT bk.rowid, facf.ref, facf.ref_supplier ';
176  $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn facf ";
177  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_bookkeeping as bk ON( bk.fk_doc = facf.rowid AND facf.rowid IN (".$this->db->sanitize(implode(',', $ids_fact))."))";
178  $sql .= " WHERE bk.code_journal IN (SELECT code FROM ".MAIN_DB_PREFIX."accounting_journal WHERE nature=3 AND entity=".$conf->entity.") ";
179  $sql .= " AND facf.entity = ".$conf->entity;
180  $sql .= " AND ( ";
181  if ($object->code_compta != "") {
182  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta)."' ";
183  }
184  if ($object->code_compta != "" && $object->code_compta_fournisseur != "") {
185  $sql .= " OR ";
186  }
187  if ($object->code_compta_fournisseur != "") {
188  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta_fournisseur)."' ";
189  }
190  $sql .= ") ";
191 
192  $resql2 = $this->db->query($sql);
193  if ($resql2) {
194  while ($obj2 = $this->db->fetch_object($resql2)) {
195  $ids[$obj2->rowid] = $obj2->rowid;
196  }
197  $this->db->free($resql2);
198  } else {
199  $this->errors[] = $this->db->lasterror;
200  return -1;
201  }
202  }
203  } elseif ($obj->type == 'payment') {
204  $sql = 'SELECT DISTINCT bk.rowid, fac.ref, fac.ref, pay.fk_bank, fac.rowid as fact_id';
205  $sql .= " FROM ".MAIN_DB_PREFIX."facture fac ";
206  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."paiement_facture as payfac ON payfac.fk_facture=fac.rowid";
207  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."paiement as pay ON payfac.fk_paiement=pay.rowid";
208  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_bookkeeping as bk ON (bk.fk_doc = pay.fk_bank AND bk.code_journal='".$this->db->escape($obj->code_journal)."')";
209  $sql .= " WHERE payfac.fk_paiement = '".$this->db->escape($obj->url_id)."' ";
210  $sql .= " AND bk.code_journal IN (SELECT code FROM ".MAIN_DB_PREFIX."accounting_journal WHERE nature=4 AND entity=".$conf->entity.") ";
211  $sql .= " AND fac.entity IN (".getEntity('invoice', 0).")"; // We don't share object for accountancy
212  $sql .= " AND ( ";
213  if ($object->code_compta != "") {
214  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta)."' ";
215  }
216  if ($object->code_compta != "" && $object->code_compta_fournisseur != "") {
217  $sql .= " OR ";
218  }
219  if ($object->code_compta_fournisseur != "") {
220  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta_fournisseur)."' ";
221  }
222  $sql .= " )";
223 
224  $resql2 = $this->db->query($sql);
225  if ($resql2) {
226  while ($obj2 = $this->db->fetch_object($resql2)) {
227  $ids[$obj2->rowid] = $obj2->rowid;
228  $ids_fact[] = $obj2->fact_id;
229  }
230  } else {
231  $this->errors[] = $this->db->lasterror;
232  return -1;
233  }
234  if (count($ids_fact)) {
235  $sql = 'SELECT bk.rowid, fac.ref, fac.ref_supplier ';
236  $sql .= " FROM ".MAIN_DB_PREFIX."facture fac ";
237  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."accounting_bookkeeping as bk ON( bk.fk_doc = fac.rowid AND fac.rowid IN (".$this->db->sanitize(implode(',', $ids_fact))."))";
238  $sql .= " WHERE code_journal IN (SELECT code FROM ".MAIN_DB_PREFIX."accounting_journal WHERE nature=2 AND entity=".$conf->entity.") ";
239  $sql .= " AND fac.entity IN (".getEntity('invoice', 0).")"; // We don't share object for accountancy
240  $sql .= " AND ( ";
241  if ($object->code_compta != "") {
242  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta)."' ";
243  }
244  if ($object->code_compta != "" && $object->code_compta_fournisseur != "") {
245  $sql .= " OR ";
246  }
247  if ($object->code_compta_fournisseur != "") {
248  $sql .= " bk.subledger_account = '".$this->db->escape($object->code_compta_fournisseur)."' ";
249  }
250  $sql .= " ) ";
251 
252  $resql2 = $this->db->query($sql);
253  if ($resql2) {
254  while ($obj2 = $this->db->fetch_object($resql2)) {
255  $ids[$obj2->rowid] = $obj2->rowid;
256  }
257  $this->db->free($resql2);
258  } else {
259  $this->errors[] = $this->db->lasterror;
260  return -1;
261  }
262  }
263  }
264 
265  if (count($ids) > 1) {
266  $result = $this->updateLettering($ids);
267  }
268  }
269  $this->db->free($resql);
270  }
271  if ($error) {
272  foreach ($this->errors as $errmsg) {
273  dol_syslog(__METHOD__.' '.$errmsg, LOG_ERR);
274  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
275  }
276  return -1 * $error;
277  } else {
278  return 1;
279  }
280  }
281 
288  public function updateLettering($ids = array(), $notrigger = false)
289  {
290  $error = 0;
291  $lettre = 'AAA';
292 
293  $sql = "SELECT DISTINCT ab2.lettering_code";
294  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
295  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab2 ON ab2.subledger_account = ab.subledger_account";
296  $sql .= " WHERE ab.rowid IN (" . $this->db->sanitize(implode(',', $ids)) . ")";
297  $sql .= " AND ab2.lettering_code != ''";
298  $sql .= " ORDER BY ab2.lettering_code DESC";
299  $sql .= " LIMIT 1 ";
300 
301  $resqla = $this->db->query($sql);
302  if ($resqla) {
303  $obj = $this->db->fetch_object($resqla);
304  $lettre = (empty($obj->lettering_code) ? $lettre : $obj->lettering_code);
305  if (!empty($obj->lettering_code)) {
306  $lettre++;
307  }
308  $this->db->free($resqla);
309  } else {
310  $this->errors[] = 'Error'.$this->db->lasterror();
311  $error++;
312  }
313 
314  $sql = "SELECT SUM(ABS(debit)) as deb, SUM(ABS(credit)) as cred FROM ".MAIN_DB_PREFIX."accounting_bookkeeping WHERE ";
315  $sql .= " rowid IN (".$this->db->sanitize(implode(',', $ids)).") AND lettering_code IS NULL AND subledger_account != ''";
316  $resqlb = $this->db->query($sql);
317  if ($resqlb) {
318  $obj = $this->db->fetch_object($resqlb);
319  if (!(round(abs($obj->deb), 2) === round(abs($obj->cred), 2))) {
320  $this->errors[] = 'Total not exacts '.round(abs($obj->deb), 2).' vs '.round(abs($obj->cred), 2);
321  $error++;
322  }
323  $this->db->free($resqlb);
324  } else {
325  $this->errors[] = 'Erreur sql'.$this->db->lasterror();
326  $error++;
327  }
328 
329  // Update request
330 
331  $now = dol_now();
332 
333  if (!$error) {
334  $sql = "UPDATE ".MAIN_DB_PREFIX."accounting_bookkeeping SET";
335  $sql .= " lettering_code='".$this->db->escape($lettre)."'";
336  $sql .= ", date_lettering = '".$this->db->idate($now)."'"; // todo correct date it's false
337  $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $ids)).") AND lettering_code IS NULL AND subledger_account != ''";
338 
339  dol_syslog(get_class($this)."::update", LOG_DEBUG);
340  $resql = $this->db->query($sql);
341  if (!$resql) {
342  $error++;
343  $this->errors[] = "Error ".$this->db->lasterror();
344  }
345  }
346 
347  // Commit or rollback
348  if ($error) {
349  foreach ($this->errors as $errmsg) {
350  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
351  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
352  }
353  return -1 * $error;
354  } else {
355  return 1;
356  }
357  }
358 
365  public function deleteLettering($ids, $notrigger = false)
366  {
367  $error = 0;
368 
369  $sql = "UPDATE ".MAIN_DB_PREFIX."accounting_bookkeeping SET";
370  $sql .= " lettering_code = NULL";
371  $sql .= ", date_lettering = NULL";
372  $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $ids)).")";
373  $sql .= " AND subledger_account != ''";
374 
375  dol_syslog(get_class($this)."::update", LOG_DEBUG);
376  $resql = $this->db->query($sql);
377  if (!$resql) {
378  $error++;
379  $this->errors[] = "Error ".$this->db->lasterror();
380  }
381 
382  // Commit or rollback
383  if ($error) {
384  foreach ($this->errors as $errmsg) {
385  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
386  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
387  }
388  return -1 * $error;
389  } else {
390  return 1;
391  }
392  }
393 
401  public function bookkeepingLetteringAll($bookkeeping_ids, $unlettering = false)
402  {
403  dol_syslog(__METHOD__ . " - ", LOG_DEBUG);
404 
405  $error = 0;
406  $errors = array();
407  $nb_lettering = 0;
408 
409  $result = $this->bookkeepingLettering($bookkeeping_ids, $unlettering);
410  if ($result < 0) {
411  $error++;
412  $errors = array_merge($errors, $this->errors);
413  $nb_lettering += abs($result) - 2;
414  } else {
415  $nb_lettering += $result;
416  }
417 
418  if ($error) {
419  $this->errors = $errors;
420  return -2 - $nb_lettering;
421  } else {
422  return $nb_lettering;
423  }
424  }
425 
433  public function bookkeepingLettering($bookkeeping_ids, $unlettering = false)
434  {
435  global $langs;
436 
437  $this->errors = array();
438 
439  // Clean parameters
440  $bookkeeping_ids = is_array($bookkeeping_ids) ? $bookkeeping_ids : array();
441 
442  $error = 0;
443  $nb_lettering = 0;
444  $grouped_lines = $this->getLinkedLines($bookkeeping_ids);
445  if (!is_array($grouped_lines)) {
446  return -2;
447  }
448 
449  foreach ($grouped_lines as $lines) {
450  $group_error = 0;
451  $total = 0;
452  $do_it = !$unlettering;
453  $lettering_code = null;
454  $piece_num_lines = array();
455  $bookkeeping_lines = array();
456  foreach ($lines as $line_infos) {
457  $bookkeeping_lines[$line_infos['id']] = $line_infos['id'];
458  $piece_num_lines[$line_infos['piece_num']] = $line_infos['piece_num'];
459  $total += ($line_infos['credit'] > 0 ? $line_infos['credit'] : -$line_infos['debit']);
460 
461  // Check lettering code
462  if ($unlettering) {
463  if (isset($lettering_code) && $lettering_code != $line_infos['lettering_code']) {
464  $this->errors[] = $langs->trans('AccountancyErrorMismatchLetteringCode');
465  $group_error++;
466  break;
467  }
468  if (!isset($lettering_code)) $lettering_code = (string) $line_infos['lettering_code'];
469  if (!empty($line_infos['lettering_code'])) $do_it = true;
470  } elseif (!empty($line_infos['lettering_code'])) $do_it = false;
471  }
472 
473  // Check balance amount
474  if (!$group_error && !$unlettering && price2num($total) != 0) {
475  $this->errors[] = $langs->trans('AccountancyErrorMismatchBalanceAmount', $total);
476  $group_error++;
477  }
478 
479  // Lettering/Unlettering the group of bookkeeping lines
480  if (!$group_error && $do_it) {
481  if ($unlettering) $result = $this->deleteLettering($bookkeeping_lines);
482  else $result = $this->updateLettering($bookkeeping_lines);
483  if ($result < 0) {
484  $group_error++;
485  } else {
486  $nb_lettering++;
487  }
488  }
489 
490  if ($group_error) {
491  $this->errors[] = $langs->trans('AccountancyErrorLetteringBookkeeping', implode(', ', $piece_num_lines));
492  $error++;
493  }
494  }
495 
496  if ($error) {
497  return -2 - $nb_lettering;
498  } else {
499  return $nb_lettering;
500  }
501  }
502 
510  public function getLinkedLines($bookkeeping_ids, $only_has_subledger_account = true)
511  {
512  global $conf, $langs;
513  $this->errors = array();
514 
515  // Clean parameters
516  $bookkeeping_ids = is_array($bookkeeping_ids) ? $bookkeeping_ids : array();
517 
518  // Get all bookkeeping lines
519  $sql = "SELECT DISTINCT ab.doc_type, ab.fk_doc";
520  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
521  $sql .= " WHERE ab.entity IN (" . getEntity('accountancy') . ")";
522  $sql .= " AND ab.fk_doc > 0";
523  if (!empty($bookkeeping_ids)) {
524  // Get all bookkeeping lines of piece number
525  $sql .= " AND EXISTS (";
526  $sql .= " SELECT rowid";
527  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS pn";
528  $sql .= " WHERE pn.entity IN (" . getEntity('accountancy') . ")";
529  $sql .= " AND pn.rowid IN (" . $this->db->sanitize(implode(',', $bookkeeping_ids)) . ")";
530  $sql .= " AND pn.piece_num = ab.piece_num";
531  $sql .= " )";
532  }
533  if ($only_has_subledger_account) $sql .= " AND ab.subledger_account != ''";
534 
535  dol_syslog(__METHOD__ . " - Get all bookkeeping lines", LOG_DEBUG);
536  $resql = $this->db->query($sql);
537  if (!$resql) {
538  $this->errors[] = "Error " . $this->db->lasterror();
539  return -1;
540  }
541 
542  $bookkeeping_lines_by_type = array();
543  while ($obj = $this->db->fetch_object($resql)) {
544  $bookkeeping_lines_by_type[$obj->doc_type][$obj->fk_doc] = $obj->fk_doc;
545  }
546  $this->db->free($resql);
547 
548  if (empty($bookkeeping_lines_by_type)) {
549  return array();
550  }
551 
552  if (!empty($bookkeeping_lines_by_type['bank'])) {
553  $new_bookkeeping_lines_by_type = $this->getDocTypeAndFkDocFromBankLines($bookkeeping_lines_by_type['bank']);
554  if (!is_array($new_bookkeeping_lines_by_type)) {
555  return -1;
556  }
557  foreach ($new_bookkeeping_lines_by_type as $doc_type => $fk_docs) {
558  foreach ($fk_docs as $fk_doc) {
559  $bookkeeping_lines_by_type[$doc_type][$fk_doc] = $fk_doc;
560  }
561  }
562  }
563 
564  $grouped_lines = array();
565  foreach (self::$doc_type_infos as $doc_type => $doc_type_info) {
566  if (!is_array($bookkeeping_lines_by_type[$doc_type])) {
567  continue;
568  }
569 
570  // Get all document ids grouped
571  $doc_grouped = $this->getLinkedDocumentByGroup($bookkeeping_lines_by_type[$doc_type], $doc_type);
572  if (!is_array($doc_grouped)) {
573  return -1;
574  }
575 
576  // Group all lines by document/piece number
577  foreach ($doc_grouped as $doc_ids) {
578  $bank_ids = $this->getBankLinesFromFkDocAndDocType($doc_ids, $doc_type);
579  if (!is_array($bank_ids)) {
580  return -1;
581  }
582 
583  // Get all bookkeeping lines linked
584  $sql = "SELECT DISTINCT ab.rowid, ab.piece_num, ab.debit, ab.credit, ab.lettering_code";
585  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS ab";
586  $sql .= " WHERE ab.entity IN (" . getEntity('accountancy') . ")";
587  $sql .= " AND (";
588  if (!empty($bank_ids)) {
589  $sql .= " EXISTS (";
590  $sql .= " SELECT bpn.rowid";
591  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS bpn";
592  $sql .= " WHERE bpn.entity IN (" . getEntity('accountancy') . ")";
593  $sql .= " AND bpn.doc_type = 'bank'";
594  $sql .= " AND bpn.fk_doc IN (" . $this->db->sanitize(implode(',', $bank_ids)) . ")";
595  $sql .= " AND bpn.piece_num = ab.piece_num";
596  $sql .= " ) OR ";
597  }
598  $sql .= " EXISTS (";
599  $sql .= " SELECT dpn.rowid";
600  $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping AS dpn";
601  $sql .= " WHERE dpn.entity IN (" . getEntity('accountancy') . ")";
602  $sql .= " AND dpn.doc_type = '" . $this->db->escape($doc_type) . "'";
603  $sql .= " AND dpn.fk_doc IN (" . $this->db->sanitize(implode(',', $doc_ids)) . ")";
604  $sql .= " AND dpn.piece_num = ab.piece_num";
605  $sql .= " )";
606  $sql .= ")";
607  if ($only_has_subledger_account) $sql .= " AND ab.subledger_account != ''";
608 
609  dol_syslog(__METHOD__ . " - Get all bookkeeping lines linked", LOG_DEBUG);
610  $resql = $this->db->query($sql);
611  if (!$resql) {
612  $this->errors[] = "Error " . $this->db->lasterror();
613  return -1;
614  }
615 
616  $group = array();
617  while ($obj = $this->db->fetch_object($resql)) {
618  $group[$obj->rowid] = array(
619  'id' => $obj->rowid,
620  'piece_num' => $obj->piece_num,
621  'debit' => $obj->debit,
622  'credit' => $obj->credit,
623  'lettering_code' => $obj->lettering_code,
624  );
625  }
626  $this->db->free($resql);
627 
628  if (!empty($group)) $grouped_lines[] = $group;
629  }
630  }
631 
632  return $grouped_lines;
633  }
634 
641  public function getDocTypeAndFkDocFromBankLines($bank_ids)
642  {
643  dol_syslog(__METHOD__ . " - bank_ids=".json_encode($bank_ids), LOG_DEBUG);
644 
645  // Clean parameters
646  $bank_ids = is_array($bank_ids) ? $bank_ids : array();
647 
648  if (empty($bank_ids)) {
649  return array();
650  }
651 
652  $bookkeeping_lines_by_type = array();
653  foreach (self::$doc_type_infos as $doc_type => $doc_type_info) {
654  // Get all fk_doc by doc_type from bank ids
655  $sql = "SELECT DISTINCT dp." . $doc_type_info['doc_payment_table_fk_doc'] . " AS fk_doc";
656  $sql .= " FROM " . MAIN_DB_PREFIX . $doc_type_info['payment_table'] . " AS p";
657  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $doc_type_info['doc_payment_table'] . " AS dp ON dp." . $doc_type_info['doc_payment_table_fk_payment'] . " = p.rowid";
658  $sql .= " WHERE p." . $doc_type_info['payment_table_fk_bank'] . " IN (" . $this->db->sanitize(implode(',', $bank_ids)) . ")";
659  $sql .= " AND dp." . $doc_type_info['doc_payment_table_fk_doc'] . " > 0";
660 
661  dol_syslog(__METHOD__ . " - Get all fk_doc by doc_type from list of bank ids for '" . $doc_type . "'", LOG_DEBUG);
662  $resql = $this->db->query($sql);
663  if (!$resql) {
664  $this->errors[] = "Error " . $this->db->lasterror();
665  return -1;
666  }
667 
668  while ($obj = $this->db->fetch_object($resql)) {
669  $bookkeeping_lines_by_type[$doc_type][$obj->fk_doc] = $obj->fk_doc;
670  }
671  $this->db->free($resql);
672  }
673 
674  return $bookkeeping_lines_by_type;
675  }
676 
684  public function getBankLinesFromFkDocAndDocType($document_ids, $doc_type)
685  {
686  global $langs;
687 
688  dol_syslog(__METHOD__ . " - bank_ids=".json_encode($document_ids) . ", doc_type=$doc_type", LOG_DEBUG);
689 
690  // Clean parameters
691  $document_ids = is_array($document_ids) ? $document_ids : array();
692  $doc_type = trim($doc_type);
693 
694  if (empty($document_ids)) {
695  return array();
696  }
697  if (!is_array(self::$doc_type_infos[$doc_type])) {
698  $langs->load('errors');
699  $this->errors[] = $langs->trans('ErrorBadParameters');
700  return -1;
701  }
702 
703  $doc_type_info = self::$doc_type_infos[$doc_type];
704  $bank_ids = array();
705 
706  // Get all fk_doc by doc_type from bank ids
707  $sql = "SELECT DISTINCT p." . $doc_type_info['payment_table_fk_bank'] . " AS fk_doc";
708  $sql .= " FROM " . MAIN_DB_PREFIX . $doc_type_info['payment_table'] . " AS p";
709  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $doc_type_info['doc_payment_table'] . " AS dp ON dp." . $doc_type_info['doc_payment_table_fk_payment'] . " = p.rowid";
710  $sql .= " WHERE dp." . $doc_type_info['doc_payment_table_fk_doc'] . " IN (" . $this->db->sanitize(implode(',', $document_ids)) . ")";
711  $sql .= " AND p." . $doc_type_info['payment_table_fk_bank'] . " > 0";
712 
713  dol_syslog(__METHOD__ . " - Get all bank ids from list of document ids of a type '" . $doc_type . "'", LOG_DEBUG);
714  $resql = $this->db->query($sql);
715  if (!$resql) {
716  $this->errors[] = "Error " . $this->db->lasterror();
717  return -1;
718  }
719 
720  while ($obj = $this->db->fetch_object($resql)) {
721  $bank_ids[$obj->fk_doc] = $obj->fk_doc;
722  }
723  $this->db->free($resql);
724 
725  return $bank_ids;
726  }
727 
735  public function getLinkedDocumentByGroup($document_ids, $doc_type)
736  {
737  global $langs;
738 
739  // Clean parameters
740  $document_ids = is_array($document_ids) ? $document_ids : array();
741  $doc_type = trim($doc_type);
742 
743  if (empty($document_ids)) {
744  return array();
745  }
746  if (!is_array(self::$doc_type_infos[$doc_type])) {
747  $langs->load('errors');
748  $this->errors[] = $langs->trans('ErrorBadParameters');
749  return -1;
750  }
751 
752  $doc_type_info = self::$doc_type_infos[$doc_type];
753 
754  // Get document lines
755  $current_document_ids = array();
756  $link_by_element = array();
757  $element_by_link = array();
758  foreach ($doc_type_info['linked_info'] as $linked_info) {
759  $sql = "SELECT DISTINCT tl2." . $linked_info['fk_link'] . " AS fk_link, tl2." . $linked_info['fk_doc'] . " AS fk_doc";
760  $sql .= " FROM " . MAIN_DB_PREFIX . $linked_info['table'] . " AS tl";
761  $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $linked_info['table'] . " AS tl2 ON tl2." . $linked_info['fk_link'] . " = tl." . $linked_info['fk_link'];
762  $sql .= " WHERE tl." . $linked_info['fk_doc'] . " IN (" . $this->db->sanitize(implode(',', $document_ids)) . ")";
763 
764  dol_syslog(__METHOD__ . " - Get document lines", LOG_DEBUG);
765  $resql = $this->db->query($sql);
766  if (!$resql) {
767  $this->errors[] = "Error " . $this->db->lasterror();
768  return -1;
769  }
770 
771  $is_fk_link_is_also_fk_doc = !empty($linked_info['is_fk_link_is_also_fk_doc']);
772  while ($obj = $this->db->fetch_object($resql)) {
773  $current_document_ids[$obj->fk_doc] = $obj->fk_doc;
774 
775  $link_key = $linked_info['prefix'] . $obj->fk_link;
776  $element_by_link[$link_key][$obj->fk_doc] = $obj->fk_doc;
777  $link_by_element[$obj->fk_doc][$link_key] = $link_key;
778  if ($is_fk_link_is_also_fk_doc) {
779  $element_by_link[$link_key][$obj->fk_link] = $obj->fk_link;
780  $link_by_element[$obj->fk_link][$link_key] = $link_key;
781  }
782  }
783  $this->db->free($resql);
784  }
785 
786  if (count(array_diff($document_ids, $current_document_ids))) {
787  return $this->getLinkedDocumentByGroup($current_document_ids, $doc_type);
788  }
789 
790  return $this->getGroupElements($link_by_element, $element_by_link);
791  }
792 
802  public function getGroupElements(&$link_by_element, &$element_by_link, $link_key = '', &$current_group = array())
803  {
804  $grouped_elements = array();
805  if (!empty($link_key) && !isset($element_by_link[$link_key])) {
806  // Return if specific link key not found
807  return $grouped_elements;
808  }
809 
810  if (empty($link_key)) {
811  // Save list when is the begin of recursive function
812  $save_link_by_element = $link_by_element;
813  $save_element_by_link = $element_by_link;
814  }
815 
816  do {
817  // Get current element id, get this payment id list and delete the entry
818  $current_link_key = !empty($link_key) ? $link_key : array_keys($element_by_link)[0];
819  $element_ids = $element_by_link[$current_link_key];
820  unset($element_by_link[$current_link_key]);
821 
822  foreach ($element_ids as $element_id) {
823  // Continue if element id in not found
824  if (!isset($link_by_element[$element_id])) continue;
825 
826  // Set the element in the current group
827  $current_group[$element_id] = $element_id;
828 
829  // Get current link keys, get this element id list and delete the entry
830  $link_keys = $link_by_element[$element_id];
831  unset($link_by_element[$element_id]);
832 
833  // Set element id on the current group for each link key of the element
834  foreach ($link_keys as $key) {
835  $this->getGroupElements($link_by_element, $element_by_link, $key, $current_group);
836  }
837  }
838 
839  if (empty($link_key)) {
840  // Save current group and reset the current group when is the begin of recursive function
841  $grouped_elements[] = $current_group;
842  $current_group = array();
843  }
844  } while (!empty($element_by_link) && empty($link_key));
845 
846  if (empty($link_key)) {
847  // Restore list when is the begin of recursive function
848  $link_by_element = $save_link_by_element;
849  $element_by_link = $save_element_by_link;
850  }
851 
852  return $grouped_elements;
853  }
854 }
Class to manage Ledger (General Ledger and Subledger)
Class Lettering.
updateLettering($ids=array(), $notrigger=false)
getDocTypeAndFkDocFromBankLines($bank_ids)
Get all fk_doc by doc_type from list of bank ids.
letteringThirdparty($socid)
letteringThirdparty
bookkeepingLetteringAll($bookkeeping_ids, $unlettering=false)
Lettering bookkeeping lines all types.
getGroupElements(&$link_by_element, &$element_by_link, $link_key='', &$current_group=array())
Get element ids grouped by link or element in common.
bookkeepingLettering($bookkeeping_ids, $unlettering=false)
Lettering bookkeeping lines.
getLinkedDocumentByGroup($document_ids, $doc_type)
Get all linked document ids by group and type.
deleteLettering($ids, $notrigger=false)
getBankLinesFromFkDocAndDocType($document_ids, $doc_type)
Get all bank ids from list of document ids of a type.
getLinkedLines($bookkeeping_ids, $only_has_subledger_account=true)
Lettering bookkeeping lines.
Class to manage third parties objects (customers, suppliers, prospects...)
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_now($mode='auto')
Return date for now.
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.
$conf db
API class for accounts.
Definition: inc.php:41