dolibarr 24.0.0-beta
fournisseur.facture.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2010-2023 Juanjo Menent <jmenent@simnandez.es>
8 * Copyright (C) 2013-2019 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2015 Bahfir Abbes <bafbes@gmail.com>
12 * Copyright (C) 2015-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2016-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
14 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
15 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
16 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
17 * Copyright (C) 2023 Nick Fragoulis
18 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
19 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
41require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
42require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
43require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.ligne.class.php';
44require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
45require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
46
47if (isModEnabled('accounting')) {
48 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
49 require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
50}
51
56{
57 use CommonSubtotal;
58
62 public $element = 'invoice_supplier';
63
67 public $table_element = 'facture_fourn';
68
72 public $table_element_line = 'facture_fourn_det';
73
77 public $class_element_line = 'SupplierInvoiceLine';
81 public $fk_element = 'fk_facture_fourn';
82
86 public $picto = 'supplier_invoice';
87
92 public $restrictiononfksoc = 1;
93
97 protected $table_ref_field = 'ref';
98
102 public $rowid;
103
107 public $ref;
108
112 public $ref_supplier;
113
118 public $libelle;
122 public $label;
123
124 //Check constants for types
125 public $type = self::TYPE_STANDARD;
126
133 public $statut;
134
140 public $status;
141
148 public $fk_statut;
149
155 public $paye;
160 public $paid;
161
166 public $author;
167
172 public $fk_user_valid;
173
179 public $datec;
180
186 public $date_echeance;
187
192 public $amount = 0;
197 public $remise = 0;
198
203 public $tva;
204
205 // Warning: Do not set default value into property definition. it must stay null.
206 // For example to avoid to have substitution done when object is generic and not yet defined.
208 public $localtax1;
210 public $localtax2;
212 public $total_ht;
214 public $total_tva;
216 public $total_localtax1;
218 public $total_localtax2;
220 public $total_ttc;
221
227 public $note;
229 public $note_private;
231 public $note_public;
233 public $propalid;
234
238 public $fk_account; // default bank account
239
243 public $transport_mode_id;
244
248 public $vat_reverse_charge;
249
253 public $extraparams = array();
254
258 public $lines = array();
259
264 public $fournisseur;
265
267
270 public $fk_facture_source;
271
273 public $fac_rec;
275 public $fk_fac_rec_source;
276
277 public $fields = array(
278 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
279 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
280 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
281 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
282 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
283 'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
284 'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
285 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
286 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
287 'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
288 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
289 'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
290 'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
291 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
292 'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
293 'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
294 'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
295 'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
296 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
297 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
298 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
299 'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
300 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
301 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
302 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
303 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
304 'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
305 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
306 'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
307 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
308 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
309 'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
310 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
311 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
312 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
313 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
314 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
315 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
316 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
317 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
318 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
319 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
320 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
321 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
322 'date_pointoftax' => array('type' => 'date', 'label' => 'Date pointoftax', 'enabled' => 1, 'visible' => -1, 'position' => 235),
323 'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
324 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
325 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
326 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
327 );
328
329
333 const TYPE_STANDARD = 0;
334
339
344
348 const TYPE_DEPOSIT = 3;
349
353 const STATUS_DRAFT = 0;
354
359
367 const STATUS_CLOSED = 2;
368
377
378 const CLOSECODE_DISCOUNTVAT = 'discount_vat';
379 const CLOSECODE_BADCREDIT = 'badsupplier';
380 const CLOSECODE_ABANDONED = 'abandon';
381 const CLOSECODE_REPLACED = 'replaced';
382
383
389 public function __construct($db)
390 {
391 $this->db = $db;
392
393 $this->ismultientitymanaged = 1;
394 }
395
402 public function create($user)
403 {
404 global $langs, $conf, $hookmanager;
405
406 $error = 0;
407 $now = dol_now();
408
409 // Clean parameters
410 if (isset($this->ref_supplier)) {
411 $this->ref_supplier = trim($this->ref_supplier);
412 }
413 if (empty($this->type)) {
414 $this->type = self::TYPE_STANDARD;
415 }
416 if (empty($this->date)) {
417 $this->date = $now;
418 }
419
420 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
421 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
422 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
423 } else {
424 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
425 }
426 if (empty($this->fk_multicurrency)) {
427 $this->multicurrency_code = $conf->currency;
428 $this->fk_multicurrency = 0;
429 $this->multicurrency_tx = 1;
430 }
431 $this->entity = setEntity($this);
432
433 $this->db->begin();
434
435 // Defaults
436 $originaldatewhen = 0;
437 $nextdatewhen = 0;
438 $previousdaynextdatewhen = 0;
439 $_facrec = null;
440
441 // Create invoice from a template recurring invoice
442 if ($this->fac_rec > 0) {
443 $this->fk_fac_rec_source = $this->fac_rec;
444
445 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
446 $_facrec = new FactureFournisseurRec($this->db);
447 $result = $_facrec->fetch($this->fac_rec);
448 $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
449
450 // Define some dates
451 if (!empty($_facrec->frequency)) {
452 $originaldatewhen = $_facrec->date_when;
453 $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
454 $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
455 $this->socid = $_facrec->socid;
456 }
457
458 $this->entity = $_facrec->entity; // Invoice created in same entity than template
459
460 // Fields coming from GUI
461 // @TODO Value of template should be used as default value on the form on the GUI, and we should here always use the value from GUI
462 // set by posted page with $object->xxx = ... and this section should be removed.
463 $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
464 $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
465 $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
466 $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
467 $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
468 $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
469 $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
470
471 // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
472 $this->total_ht = $_facrec->total_ht;
473 $this->total_ttc = $_facrec->total_ttc;
474
475 // Fields always coming from template
476 $this->fk_incoterms = $_facrec->fk_incoterms;
477 $this->location_incoterms = $_facrec->location_incoterms;
478
479 // Clean parameters
480 if (! $this->type) {
481 $this->type = self::TYPE_STANDARD;
482 }
483 $this->note_public = trim($this->note_public);
484 $this->note_private = trim($this->note_private);
485 $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
486
487 $this->array_options = $_facrec->array_options;
488
489 if (! $this->mode_reglement_id) {
490 $this->mode_reglement_id = 0;
491 }
492 $this->status = self::STATUS_DRAFT;
493 $this->statut = self::STATUS_DRAFT; // deprecated
494
495 $this->linked_objects = $_facrec->linkedObjectsIds;
496 // We do not add link to template invoice or next invoice will be linked to all generated invoices
497 //$this->linked_objects['facturerec'][0] = $this->fac_rec;
498
499 $forceduedate = $this->calculate_date_lim_reglement();
500
501 // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
502 if ($_facrec->frequency > 0) {
503 $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
504 dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
505 if (empty($_facrec->date_when)) {
506 $_facrec->date_when = $now;
507 }
508 $next_date = $_facrec->getNextDate(); // Calculate next date
509 $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
510 //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
511 $result = $_facrec->setNextDate($next_date, 1);
512 }
513
514 // Define lang of customer
515 $outputlangs = $langs;
516 $newlang = '';
517
518 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
519 $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
520 }
521 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && property_exists($this, 'default_lang') && isset($this->default_lang)) { // @phan-suppress-current-line PhanUndeclaredProperty
522 $newlang = $this->default_lang; // for thirdparty @phan-suppress-current-line PhanUndeclaredProperty
523 }
524 if (!empty($newlang)) {
525 $outputlangs = new Translate("", $conf);
526 $outputlangs->setDefaultLang($newlang);
527 } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
528 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
529 $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
530 $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
531 $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
532 $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
533 $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
534 $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
535 $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
536 $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
537 $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
538 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
539 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
540 $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
541 $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
542 $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
543
544 complete_substitutions_array($substitutionarray, $outputlangs);
545
546 $this->note_public = make_substitutions($this->note_public, $substitutionarray);
547 $this->note_private = make_substitutions($this->note_private, $substitutionarray);
548 }
549
550 // Define due date if not already defined
551 if (!empty($forceduedate)) {
552 $this->date_echeance = $forceduedate;
553 }
554
555 $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
556 $sql .= "ref";
557 $sql .= ", ref_supplier";
558 $sql .= ", ref_ext";
559 $sql .= ", entity";
560 $sql .= ", type";
561 $sql .= ", subtype";
562 $sql .= ", libelle";
563 $sql .= ", fk_soc";
564 $sql .= ", datec";
565 $sql .= ", datef";
566 $sql .= ", vat_reverse_charge";
567 $sql .= ", fk_projet";
568 $sql .= ", fk_cond_reglement";
569 $sql .= ", fk_mode_reglement";
570 $sql .= ", fk_account";
571 $sql .= ", note_private";
572 $sql .= ", note_public";
573 $sql .= ", fk_user_author";
574 $sql .= ", date_lim_reglement";
575 $sql .= ", fk_incoterms, location_incoterms";
576 $sql .= ", fk_multicurrency";
577 $sql .= ", multicurrency_code";
578 $sql .= ", multicurrency_tx";
579 $sql .= ", fk_facture_source";
580 $sql .= ", fk_fac_rec_source";
581 $sql .= ")";
582 $sql .= " VALUES (";
583 $sql .= "'(PROV)'";
584 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
585 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
586 $sql .= ", ".((int) $this->entity);
587 $sql .= ", '".$this->db->escape((string) $this->type)."'";
588 $sql .= ", ".(isset($this->subtype) ? (int) $this->subtype : "null");
589 $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
590 $sql .= ", ".((int) $this->socid);
591 $sql .= ", '".$this->db->idate($now)."'";
592 $sql .= ", '".$this->db->idate($this->date)."'";
593 $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0);
594 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
595 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
596 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
597 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
598 $sql .= ", '".$this->db->escape($this->note_private)."'";
599 $sql .= ", '".$this->db->escape($this->note_public)."'";
600 $sql .= ", ".((int) $user->id).",";
601 $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
602 $sql .= ", ".(int) $this->fk_incoterms;
603 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
604 $sql .= ", ".(int) $this->fk_multicurrency;
605 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
606 $sql .= ", ".(float) $this->multicurrency_tx;
607 $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
608 $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
609 $sql .= ")";
610
611 dol_syslog(get_class($this)."::create", LOG_DEBUG);
612 $resql = $this->db->query($sql);
613 if ($resql) {
614 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
615
616 // Update ref with new one
617 $this->ref = '(PROV'.$this->id.')';
618 $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
619
620 dol_syslog(get_class($this)."::create", LOG_DEBUG);
621 $resql = $this->db->query($sql);
622 if (!$resql) {
623 $error++;
624 }
625
626 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
627 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
628 }
629
630 // Add object linked
631 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
632 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
633 if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
634 foreach ($tmp_origin_id as $origin_id) {
635 $ret = $this->add_object_linked($origin, $origin_id);
636 if (!$ret) {
637 dol_print_error($this->db);
638 $error++;
639 }
640 }
641 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
642 $origin_id = $tmp_origin_id;
643 $ret = $this->add_object_linked($origin, $origin_id);
644 if (!$ret) {
645 dol_print_error($this->db);
646 $error++;
647 }
648 }
649 }
650 }
651
652 if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
653 dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
654 foreach ($this->lines as $i => $val) {
655 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
656 $sql .= " VALUES (".((int) $this->id).", ".((int) $this->lines[$i]->special_code).", ".($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL').')';
657
658 $resql_insert = $this->db->query($sql);
659 if ($resql_insert) {
660 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
661
662 $res = $this->updateline(
663 $idligne,
664 $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
665 $this->lines[$i]->subprice,
666 $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
667 $this->lines[$i]->localtax1_tx,
668 $this->lines[$i]->localtax2_tx,
669 $this->lines[$i]->qty,
670 $this->lines[$i]->fk_product,
671 'HT',
672 (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
673 $this->lines[$i]->product_type,
674 $this->lines[$i]->remise_percent,
675 0,
676 $this->lines[$i]->date_start,
677 $this->lines[$i]->date_end,
678 $this->lines[$i]->array_options,
679 $this->lines[$i]->fk_unit,
680 $this->lines[$i]->multicurrency_subprice,
681 $this->lines[$i]->ref_supplier
682 );
683 } else {
684 $this->error = $this->db->lasterror();
685 $this->db->rollback();
686 return -5;
687 }
688 }
689 } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
690 dol_syslog("There is ".count($this->lines)." lines that are array lines");
691 foreach ($this->lines as $i => $val) {
692 $line = $this->lines[$i];
693
694 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
695 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
696 if (!is_object($line)) {
697 $line = (object) $line;
698 }
699
700 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
701 $sql .= " VALUES (".((int) $this->id).", ".((int) $this->lines[$i]->special_code).", ".($this->lines[$i]->fk_remise_except > 0 ? ((int) $this->lines[$i]->fk_remise_except) : 'NULL').')';
702
703 $resql_insert = $this->db->query($sql);
704 if ($resql_insert) {
705 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
706
707 $this->updateline(
708 $idligne,
709 $line->desc ? $line->desc : $line->description,
710 $line->subprice,
711 $line->tva_tx,
712 $line->localtax1_tx,
713 $line->localtax2_tx,
714 $line->qty,
715 $line->fk_product,
716 'HT',
717 (!empty($line->info_bits) ? $line->info_bits : ''),
718 $line->product_type,
719 $line->remise_percent,
720 0,
721 $line->date_start,
722 $line->date_end,
723 $line->array_options,
724 $line->fk_unit,
725 $line->multicurrency_subprice,
726 $line->ref_supplier
727 );
728 } else {
729 $this->error = $this->db->lasterror();
730 $this->db->rollback();
731 return -5;
732 }
733 }
734 }
735
736 /*
737 * Insert lines of template invoices
738 */
739 if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
740 foreach ($_facrec->lines as $i => $val) {
741 $product_type = $_facrec->lines[$i]->product_type;
742 if ($_facrec->lines[$i]->fk_product) {
743 $prod = new Product($this->db);
744 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
745 }
746
747 // For line from template invoice, we use data from template invoice
748 /*
749 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
750 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
751 if (empty($tva_tx)) $tva_npr=0;
752 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
753 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
754 */
755 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
756 $tva_npr = $_facrec->lines[$i]->info_bits;
757 if (empty($tva_tx)) {
758 $tva_npr = 0;
759 }
760 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
761 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
762
763 // $fk_product_fournisseur_price not used and does not exist on line
764 // $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
765 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
766
767 // If buyprice not defined from template invoice, we try to guess the best value
768 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
769 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
770 $producttmp = new ProductFournisseur($this->db);
771 $producttmp->fetch($_facrec->lines[$i]->fk_product);
772
773 // If margin module defined on costprice, we try the costprice
774 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
775 // else we get the best supplier price
776 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
777 $buyprice = $producttmp->cost_price;
778 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
779 $buyprice = $producttmp->pmp;
780 } else {
781 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
782 if ($producttmp->product_fourn_price_id > 0) {
783 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
784 }
785 }
786 }
787 }
788
789 $result_insert = $this->addline(
790 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
791 $_facrec->lines[$i]->pu_ht,
792 $tva_tx,
793 $localtax1_tx,
794 $localtax2_tx,
795 $_facrec->lines[$i]->qty,
796 $_facrec->lines[$i]->fk_product,
797 $_facrec->lines[$i]->remise_percent,
798 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
799 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
800 0,
801 $_facrec->lines[$i]->info_bits,
802 'HT',
803 $product_type,
804 $_facrec->lines[$i]->rang,
805 0,
806 $_facrec->lines[$i]->array_options,
807 $_facrec->lines[$i]->fk_unit,
808 0,
809 0,
810 $_facrec->lines[$i]->ref_supplier,
811 $_facrec->lines[$i]->special_code,
812 0,
813 0
814 );
815 if ($result_insert < 0) {
816 $error++;
817 $this->error = $this->db->error();
818 break;
819 }
820 }
821 }
822
823
824 // Update total price
825 $result = $this->update_price(1);
826 if ($result > 0) {
827 // Actions on extra fields
828 if (!$error) {
829 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
830 if ($result < 0) {
831 $error++;
832 }
833 }
834
835 if (!$error) {
836 // Call trigger
837 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
838 if ($result < 0) {
839 $error++;
840 }
841 // End call triggers
842 }
843
844 if (!$error) {
845 $this->db->commit();
846 return $this->id;
847 } else {
848 $this->db->rollback();
849 return -4;
850 }
851 } else {
852 $this->error = $langs->trans('FailedToUpdatePrice');
853 $this->db->rollback();
854 return -3;
855 }
856 } else {
857 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
858 $this->error = $langs->trans('ErrorRefAlreadyExists');
859 $this->db->rollback();
860 return -1;
861 } else {
862 $this->error = $this->db->lasterror();
863 $this->db->rollback();
864 return -2;
865 }
866 }
867 }
868
877 public function fetch($id = 0, $ref = '', $ref_ext = '')
878 {
879 if (empty($id) && empty($ref) && empty($ref_ext)) {
880 return -1;
881 }
882
883 $sql = "SELECT";
884 $sql .= " t.rowid,";
885 $sql .= " t.ref,";
886 $sql .= " t.ref_supplier,";
887 $sql .= " t.ref_ext,";
888 $sql .= " t.entity,";
889 $sql .= " t.type,";
890 $sql .= " t.subtype,";
891 $sql .= " t.fk_soc,";
892 $sql .= " t.datec,";
893 $sql .= " t.datef,";
894 $sql .= " t.tms as datem,";
895 $sql .= " t.libelle as label,";
896 $sql .= " t.paye as paid,";
897 $sql .= " t.close_code,";
898 $sql .= " t.close_note,";
899 $sql .= " t.tva,";
900 $sql .= " t.localtax1,";
901 $sql .= " t.localtax2,";
902 $sql .= " t.total_ht,";
903 $sql .= " t.total_tva,";
904 $sql .= " t.total_ttc,";
905 $sql .= " t.fk_statut as status,";
906 $sql .= " t.fk_user_author,";
907 $sql .= " t.fk_user_valid,";
908 $sql .= " t.fk_facture_source,";
909 $sql .= " t.vat_reverse_charge,";
910 $sql .= " t.fk_fac_rec_source,";
911 $sql .= " t.fk_projet as fk_project,";
912 $sql .= " t.fk_cond_reglement,";
913 $sql .= " t.fk_account,";
914 $sql .= " t.fk_mode_reglement,";
915 $sql .= " t.date_lim_reglement,";
916 $sql .= " t.note_private,";
917 $sql .= " t.note_public,";
918 $sql .= " t.model_pdf,";
919 $sql .= " t.last_main_doc,";
920 $sql .= " t.import_key,";
921 $sql .= " t.extraparams,";
922 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
923 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
924 $sql .= ' s.nom as socnom, s.rowid as socid,';
925 $sql .= ' t.fk_incoterms, t.location_incoterms,';
926 $sql .= " i.libelle as label_incoterms,";
927 $sql .= ' t.fk_transport_mode,';
928 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
929 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
930 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
931 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
932 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
933 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
934 if ($id) {
935 $sql .= " WHERE t.rowid = ".((int) $id);
936 } else {
937 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
938 if ($ref) {
939 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
940 }
941 if ($ref_ext) {
942 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
943 }
944 }
945
946 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
947 $resql = $this->db->query($sql);
948 if ($resql) {
949 if ($this->db->num_rows($resql)) {
950 $obj = $this->db->fetch_object($resql);
951
952 $this->id = $obj->rowid;
953 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
954
955 $this->ref_supplier = $obj->ref_supplier;
956 $this->ref_ext = $obj->ref_ext;
957 $this->entity = $obj->entity;
958 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
959 $this->subtype = $obj->subtype;
960 $this->socid = $obj->fk_soc;
961 $this->date = $this->db->jdate($obj->datef);
962 $this->date_creation = $this->db->jdate($obj->datec);
963 $this->datec = $this->db->jdate($obj->datec);
964 $this->date_modification = $this->db->jdate($obj->datem);
965 $this->tms = $this->db->jdate($obj->datem);
966 $this->libelle = $obj->label; // deprecated
967 $this->label = $obj->label;
968 $this->paye = $obj->paid;
969 $this->paid = $obj->paid;
970 $this->close_code = $obj->close_code;
971 $this->close_note = $obj->close_note;
972 $this->total_localtax1 = $obj->localtax1;
973 $this->total_localtax2 = $obj->localtax2;
974 $this->total_ht = $obj->total_ht;
975 $this->total_tva = $obj->total_tva;
976 $this->total_ttc = $obj->total_ttc;
977 $this->status = $obj->status;
978 $this->statut = $obj->status; // For backward compatibility
979 $this->fk_statut = $obj->status; // For backward compatibility
980 $this->user_creation_id = $obj->fk_user_author;
981 $this->author = $obj->fk_user_author; // deprecated
982 $this->user_validation_id = $obj->fk_user_valid;
983 $this->fk_user_valid = $obj->fk_user_valid;
984 $this->fk_facture_source = $obj->fk_facture_source;
985 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
986 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
987 $this->fk_project = $obj->fk_project;
988 $this->cond_reglement_id = $obj->fk_cond_reglement;
989 $this->cond_reglement_code = $obj->cond_reglement_code;
990 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
991 $this->cond_reglement_label = $obj->cond_reglement_label;
992 $this->cond_reglement_doc = $obj->cond_reglement_doc;
993 $this->fk_account = $obj->fk_account;
994 $this->mode_reglement_id = $obj->fk_mode_reglement;
995 $this->mode_reglement_code = $obj->mode_reglement_code;
996 $this->mode_reglement = $obj->mode_reglement_label;
997 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
998 $this->note = $obj->note_private; // deprecated
999 $this->note_private = $obj->note_private;
1000 $this->note_public = $obj->note_public;
1001 $this->model_pdf = $obj->model_pdf;
1002 $this->last_main_doc = $obj->last_main_doc;
1003 $this->import_key = $obj->import_key;
1004
1005 //Incoterms
1006 $this->fk_incoterms = $obj->fk_incoterms;
1007 $this->location_incoterms = $obj->location_incoterms;
1008 $this->label_incoterms = $obj->label_incoterms;
1009 $this->transport_mode_id = $obj->fk_transport_mode;
1010
1011 // Multicurrency
1012 $this->fk_multicurrency = $obj->fk_multicurrency;
1013 $this->multicurrency_code = $obj->multicurrency_code;
1014 $this->multicurrency_tx = $obj->multicurrency_tx;
1015 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1016 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1017 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1018
1019 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1020
1021 $this->socid = $obj->socid;
1022
1023 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1024
1025 // Retrieve all extrafield
1026 // fetch optionals attributes and labels
1027 $this->fetch_optionals();
1028
1029 $result = $this->fetch_lines();
1030 if ($result < 0) {
1031 $this->error = $this->db->lasterror();
1032 return -3;
1033 }
1034 } else {
1035 $this->error = 'Bill with id '.$id.' not found';
1036 dol_syslog(get_class($this).'::fetch '.$this->error);
1037 return 0;
1038 }
1039
1040 $this->db->free($resql);
1041 return 1;
1042 } else {
1043 $this->error = "Error ".$this->db->lasterror();
1044 return -1;
1045 }
1046 }
1047
1048
1049 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1055 public function fetch_lines()
1056 {
1057 // phpcs:enable
1058 $this->lines = array();
1059
1060 $sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description as line_desc, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
1061 $sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn, f.fk_remise_except';
1062 $sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit, f.extraparams';
1063 $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.barcode as product_barcode, p.description as product_desc';
1064 $sql .= ', f.fk_code_ventilation, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc';
1065 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1066 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1067 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1068 $sql .= ' ORDER BY f.rang, f.rowid';
1069
1070 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1071
1072 $resql_rows = $this->db->query($sql);
1073 if ($resql_rows) {
1074 $num_rows = $this->db->num_rows($resql_rows);
1075 if ($num_rows) {
1076 $i = 0;
1077 while ($i < $num_rows) {
1078 $obj = $this->db->fetch_object($resql_rows);
1079
1080 $line = new SupplierInvoiceLine($this->db);
1081
1082 $line->id = $obj->rowid;
1083 $line->rowid = $obj->rowid;
1084
1085 $line->description = $obj->line_desc;
1086 $line->desc = $obj->line_desc;
1087 $line->date_start = $this->db->jdate($obj->date_start);
1088 $line->date_end = $this->db->jdate($obj->date_end);
1089
1090 $line->product_ref = $obj->product_ref;
1091 $line->ref = $obj->product_ref;
1092 $line->ref_supplier = $obj->ref_supplier;
1093 $line->libelle = $obj->label;
1094 $line->label = $obj->label;
1095 $line->product_barcode = $obj->product_barcode;
1096 $line->product_desc = $obj->product_desc;
1097 $line->subprice = $obj->pu_ht;
1098 $line->pu_ht = $obj->pu_ht; // deprecated
1099 $line->subprice_ttc = $obj->pu_ttc;
1100 $line->pu_ttc = $obj->pu_ttc; // deprcated
1101 $line->vat_src_code = $obj->vat_src_code;
1102 $line->tva_tx = $obj->tva_tx;
1103 $line->localtax1_tx = $obj->localtax1_tx;
1104 $line->localtax2_tx = $obj->localtax2_tx;
1105 $line->localtax1_type = $obj->localtax1_type;
1106 $line->localtax2_type = $obj->localtax2_type;
1107 $line->qty = $obj->qty;
1108 $line->remise_percent = $obj->remise_percent;
1109 $line->fk_remise_except = $obj->fk_remise_except;
1110 //$line->tva = $obj->total_tva; // deprecated
1111 $line->total_ht = $obj->total_ht;
1112 $line->total_ttc = $obj->total_ttc;
1113 $line->total_tva = $obj->total_tva;
1114 $line->total_localtax1 = $obj->total_localtax1;
1115 $line->total_localtax2 = $obj->total_localtax2;
1116 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1117 $line->fk_product = $obj->fk_product;
1118 $line->product_type = $obj->product_type;
1119 $line->product_label = $obj->label;
1120 $line->info_bits = $obj->info_bits;
1121 $line->fk_parent_line = $obj->fk_parent_line;
1122 $line->special_code = $obj->special_code;
1123 $line->rang = $obj->rang;
1124 $line->fk_unit = $obj->fk_unit;
1125
1126 $line->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1127
1128 // Accountancy
1129 $line->fk_code_ventilation = $obj->fk_code_ventilation;
1130
1131 // Multicurrency
1132 $line->fk_multicurrency = $obj->fk_multicurrency;
1133 $line->multicurrency_code = $obj->multicurrency_code;
1134 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1135 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1136 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1137 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1138
1139 // Extra fields
1140 $line->fetch_optionals();
1141
1142 $this->lines[$i] = $line;
1143
1144 $i++;
1145 }
1146 }
1147 $this->db->free($resql_rows);
1148 return 1;
1149 } else {
1150 $this->error = $this->db->error();
1151 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1152 return -3;
1153 }
1154 }
1155
1156
1164 public function update($user = null, $notrigger = 0)
1165 {
1166 global $langs;
1167 $error = 0;
1168
1169 // Clean parameters
1170 if (empty($this->type)) {
1171 $this->type = self::TYPE_STANDARD;
1172 }
1173 if (isset($this->ref)) {
1174 $this->ref = trim($this->ref);
1175 }
1176 if (isset($this->ref_supplier)) {
1177 $this->ref_supplier = trim($this->ref_supplier);
1178 }
1179 if (isset($this->ref_ext)) {
1180 $this->ref_ext = trim($this->ref_ext);
1181 }
1182 if (isset($this->entity)) {
1183 $this->entity = (int) $this->entity;
1184 }
1185 if (isset($this->type)) {
1186 $this->type = (int) $this->type;
1187 }
1188 if (isset($this->subtype)) {
1189 $this->subtype = (int) $this->subtype;
1190 }
1191 if (isset($this->socid)) {
1192 $this->socid = (int) $this->socid;
1193 }
1194 if (isset($this->label)) {
1195 $this->label = trim($this->label);
1196 }
1197 if (isset($this->paid)) {
1198 $this->paid = (int) (bool) $this->paid;
1199 $this->paye = $this->paid; // deprecated
1200 } elseif (isset($this->paye)) {
1201 $this->paid = (int) (bool) $this->paye;
1202 $this->paye = $this->paid; // deprecared
1203 }
1204 if (isset($this->close_code)) {
1205 $this->close_code = trim($this->close_code);
1206 }
1207 if (isset($this->close_note)) {
1208 $this->close_note = trim($this->close_note);
1209 }
1210 if (empty($this->total_ht)) {
1211 $this->total_ht = 0;
1212 }
1213 if (empty($this->total_tva)) {
1214 $this->total_tva = 0;
1215 }
1216 if (empty($this->total_localtax1)) {
1217 $this->total_localtax1 = 0;
1218 }
1219 if (empty($this->total_localtax2)) {
1220 $this->total_localtax2 = 0;
1221 }
1222 if (isset($this->total_ttc)) {
1223 $this->total_ttc = (float) $this->total_ttc;
1224 }
1225 if (isset($this->status)) {
1226 $this->status = (int) $this->status;
1227 $this->statut = $this->status;
1228 } elseif (isset($this->statut)) {
1229 $this->status = (int) $this->statut;
1230 $this->statut = $this->status;
1231 }
1232 if (isset($this->author)) { // TODO: user_creation_id?
1233 $this->author = (int) $this->author;
1234 }
1235 if (isset($this->fk_user_valid)) {
1236 $this->fk_user_valid = (int) $this->fk_user_valid;
1237 }
1238 if (isset($this->fk_facture_source)) {
1239 $this->fk_facture_source = (int) $this->fk_facture_source;
1240 }
1241 if (isset($this->fk_project)) {
1242 if (empty($this->fk_project)) {
1243 $this->fk_project = 0;
1244 } else {
1245 $this->fk_project = (int) $this->fk_project;
1246 }
1247 }
1248 if (isset($this->mode_reglement_id)) {
1249 $this->mode_reglement_id = (int) $this->mode_reglement_id;
1250 }
1251 if (isset($this->cond_reglement_id)) {
1252 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1253 }
1254 if (isset($this->note_private)) {
1255 $this->note_private = trim($this->note_private);
1256 $this->note = $this->note_private;
1257 }
1258 if (isset($this->note_public)) {
1259 $this->note_public = trim($this->note_public);
1260 }
1261 if (isset($this->model_pdf)) {
1262 $this->model_pdf = trim($this->model_pdf);
1263 }
1264 if (isset($this->import_key)) {
1265 $this->import_key = trim($this->import_key);
1266 }
1267
1268
1269 // Check parameters
1270 // Put here code to add control on parameters values
1271
1272 // Update request
1273 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1274 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1275 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1276 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1277 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1278 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1279 $sql .= " subtype=".(isset($this->subtype) ? (int) $this->subtype : "null").",";
1280 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1281 $sql .= " datec=".(dol_strlen((string) $this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1282 $sql .= " datef=".(dol_strlen((string) $this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1283 if (dol_strlen((string) $this->date_modification) != 0) {
1284 $sql .= " tms=".(dol_strlen((string) $this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').",";
1285 } elseif (dol_strlen((string) $this->tms) != 0) { // For backward compatibility
1286 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1287 }
1288 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1289 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1290 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1291 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1292 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1293 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1294 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1295 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1296 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1297 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1298 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1299 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1300 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1301 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0).",";
1302 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1303 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
1304 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1305 $sql .= " date_lim_reglement=".(dol_strlen((string) $this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1306 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1307 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1308 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1309 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1310 $sql .= " WHERE rowid=".((int) $this->id);
1311
1312 $this->db->begin();
1313
1314 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1315 $resql = $this->db->query($sql);
1316
1317 if (!$resql) {
1318 $error++;
1319
1320 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1321 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1322 } else {
1323 $this->errors[] = "Error ".$this->db->lasterror();
1324 }
1325 }
1326
1327 if (!$error) {
1328 $result = $this->insertExtraFields();
1329 if ($result < 0) {
1330 $error++;
1331 }
1332 }
1333
1334 if (!$error && !$notrigger) {
1335 // Call trigger
1336 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1337 if ($result < 0) {
1338 $error++;
1339 }
1340 // End call triggers
1341 }
1342
1343 // Commit or rollback
1344 if ($error) {
1345 foreach ($this->errors as $errmsg) {
1346 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1347 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1348 }
1349 $this->db->rollback();
1350 return -1 * $error;
1351 } else {
1352 $this->db->commit();
1353 return 1;
1354 }
1355 }
1356
1357 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1364 public function insert_discount($idremise)
1365 {
1366 // phpcs:enable
1367 global $conf, $langs;
1368
1369 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1370 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1371
1372 $this->db->begin();
1373
1374 $remise = new DiscountAbsolute($this->db);
1375 $result = $remise->fetch($idremise);
1376
1377 if ($result > 0) {
1378 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1379 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1380 $this->db->rollback();
1381 return -5;
1382 }
1383
1384 $facligne = new SupplierInvoiceLine($this->db);
1385 $facligne->fk_facture_fourn = $this->id;
1386 $facligne->fk_remise_except = $remise->id;
1387 $facligne->desc = $remise->description; // Line description
1388 $facligne->vat_src_code = $remise->vat_src_code;
1389 $facligne->tva_tx = $remise->tva_tx;
1390 $facligne->localtax1_tx = $remise->localtax1_tx;
1391 $facligne->localtax1_type = $remise->localtax1_type;
1392 $facligne->localtax2_tx = $remise->localtax1_tx;
1393 $facligne->localtax2_type = $remise->localtax1_type;
1394 $facligne->subprice = -(float) $remise->amount_ht;
1395 $facligne->fk_product = 0; // Predefined Product ID
1396 $facligne->product_type = 0;
1397 $facligne->qty = 1;
1398 $facligne->remise_percent = 0;
1399 $facligne->rang = -1;
1400 $facligne->info_bits = 2;
1401
1402 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1403 $facligne->rang = 1;
1404 $linecount = count($this->lines);
1405 for ($ii = 1; $ii <= $linecount; $ii++) {
1406 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1407 }
1408 }
1409
1410 // Get buy/cost price of invoice that is source of discount
1411 if ($remise->fk_invoice_supplier_source > 0) {
1412 $srcinvoice = new FactureFournisseur($this->db);
1413 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1414 $totalcostpriceofinvoice = 0;
1415 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1416 $formmargin = new FormMargin($this->db);
1417 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1418 $facligne->pa_ht = $arraytmp['pa_total'];
1419 }
1420
1421 $facligne->total_ht = -(float) $remise->amount_ht;
1422 $facligne->total_tva = -(float) $remise->amount_tva;
1423 $facligne->total_ttc = -(float) $remise->amount_ttc;
1424 $facligne->total_localtax1 = -(float) $remise->total_localtax1;
1425 $facligne->total_localtax2 = -(float) $remise->total_localtax2;
1426
1427 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1428 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1429 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1430 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1431
1432 $lineid = $facligne->insert();
1433 if ($lineid > 0) {
1434 $result = $this->update_price(1);
1435 if ($result > 0) {
1436 // Create link between discount and invoice line
1437 $result = $remise->link_to_invoice($lineid, 0);
1438 if ($result < 0) {
1439 $this->error = $remise->error;
1440 $this->db->rollback();
1441 return -4;
1442 }
1443
1444 $this->db->commit();
1445 return 1;
1446 } else {
1447 $this->error = $facligne->error;
1448 $this->db->rollback();
1449 return -1;
1450 }
1451 } else {
1452 $this->error = $facligne->error;
1453 $this->db->rollback();
1454 return -2;
1455 }
1456 } else {
1457 $this->db->rollback();
1458 return -3;
1459 }
1460 }
1461
1462
1470 public function delete(User $user, $notrigger = 0)
1471 {
1472 global $conf;
1473
1474 $rowid = $this->id;
1475
1476 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1477
1478 // TODO Test if there is at least on payment. If yes, refuse to delete.
1479
1480 $error = 0;
1481 $this->db->begin();
1482
1483 if (!$notrigger) {
1484 // Call trigger
1485 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1486 if ($result < 0) {
1487 $this->db->rollback();
1488 return -1;
1489 }
1490 // Fin appel triggers
1491 }
1492
1493 // Remove linked categories.
1494 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_invoice";
1495 $sql .= " WHERE fk_supplier_invoice = ".((int) $this->id);
1496
1497 $result = $this->db->query($sql);
1498 if (!$result) {
1499 $error++;
1500 $this->error = $this->db->lasterror();
1501 $this->errors[] = $this->error;
1502 }
1503
1504 if (!$error) {
1505 // If invoice was converted into a discount not yet consumed, we remove discount
1506 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1507 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1508 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1509 $resql = $this->db->query($sql);
1510
1511 // If invoice has consumned discounts
1512 $this->fetch_lines();
1513 $list_rowid_det = array();
1514 foreach ($this->lines as $key => $invoiceline) {
1515 $list_rowid_det[] = $invoiceline->id;
1516 }
1517
1518 // Consumned discounts are freed
1519 if (count($list_rowid_det)) {
1520 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1521 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1522 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1523
1524 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1525 if (!$this->db->query($sql)) {
1526 $error++;
1527 }
1528 }
1529 }
1530
1531 if (!$error) {
1532 $main = MAIN_DB_PREFIX.'facture_fourn_det';
1533 $ef = $main."_extrafields";
1534 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1535 $resqlef = $this->db->query($sqlef);
1536 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1537 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1538 $resql = $this->db->query($sql);
1539 if ($resqlef && $resql) {
1540 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1541 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1542 $resql2 = $this->db->query($sql);
1543 if (!$resql2) {
1544 $error++;
1545 }
1546 } else {
1547 $error++;
1548 }
1549 }
1550
1551 if (!$error) {
1552 // Delete linked object
1553 $res = $this->deleteObjectLinked();
1554 if ($res < 0) {
1555 $error++;
1556 }
1557 }
1558
1559 if (!$error) {
1560 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1561 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1562 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1563
1564 // We remove directory
1565 if ($conf->fournisseur->facture->dir_output) {
1566 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1567
1568 $ref = dol_sanitizeFileName($this->ref);
1569 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1570 $file = $dir."/".$ref.".pdf";
1571 if (file_exists($file)) {
1572 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1573 $this->error = 'ErrorFailToDeleteFile';
1574 $error++;
1575 }
1576 }
1577 if (file_exists($dir)) {
1578 $res = @dol_delete_dir_recursive($dir);
1579
1580 if (!$res) {
1581 $this->error = 'ErrorFailToDeleteDir';
1582 $error++;
1583 }
1584 }
1585 }
1586 }
1587
1588 // Remove extrafields
1589 if (!$error) {
1590 $result = $this->deleteExtraFields();
1591 if ($result < 0) {
1592 $error++;
1593 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1594 }
1595 }
1596
1597 if (!$error) {
1598 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1599 $this->db->commit();
1600 return 1;
1601 } else {
1602 $this->error = $this->db->lasterror();
1603 $this->db->rollback();
1604 return -$error;
1605 }
1606 }
1607
1608
1609 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1620 public function set_paid($user, $close_code = '', $close_note = '')
1621 {
1622 // phpcs:enable
1623 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1624 return $this->setPaid($user, $close_code, $close_note);
1625 }
1626
1635 public function setPaid($user, $close_code = '', $close_note = '')
1636 {
1637 $error = 0;
1638
1639 if ($this->paid != 1) {
1640 $this->db->begin();
1641
1642 $now = dol_now();
1643
1644 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1645
1646 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1647 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1648 if (!$close_code) {
1649 $sql .= ', paye=1';
1650 }
1651 if ($close_code) {
1652 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1653 }
1654 if ($close_note) {
1655 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1656 }
1657 $sql .= ', fk_user_closing = '.((int) $user->id);
1658 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1659 $sql .= ' WHERE rowid = '.((int) $this->id);
1660
1661 $resql = $this->db->query($sql);
1662 if ($resql) {
1663 // Call trigger
1664 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1665 if ($result < 0) {
1666 $error++;
1667 }
1668 // End call triggers
1669 } else {
1670 $error++;
1671 $this->error = $this->db->error();
1672 dol_print_error($this->db);
1673 }
1674
1675 if (!$error) {
1676 $this->db->commit();
1677 return 1;
1678 } else {
1679 $this->db->rollback();
1680 return -1;
1681 }
1682 } else {
1683 return 0;
1684 }
1685 }
1686
1687 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1698 public function set_unpaid($user)
1699 {
1700 // phpcs:enable
1701 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1702 return $this->setUnpaid($user);
1703 }
1704
1713 public function setUnpaid($user)
1714 {
1715 $error = 0;
1716
1717 $this->db->begin();
1718
1719 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1720 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1721 $sql .= ' date_closing=null,';
1722 $sql .= ' fk_user_closing=null';
1723 $sql .= ' WHERE rowid = '.((int) $this->id);
1724
1725 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1726 $resql = $this->db->query($sql);
1727 if ($resql) {
1728 // Call trigger
1729 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1730 if ($result < 0) {
1731 $error++;
1732 }
1733 // End call triggers
1734 } else {
1735 $error++;
1736 $this->error = $this->db->error();
1737 dol_print_error($this->db);
1738 }
1739
1740 if (!$error) {
1741 $this->db->commit();
1742 return 1;
1743 } else {
1744 $this->db->rollback();
1745 return -1;
1746 }
1747 }
1748
1759 public function setCanceled($user, $close_code = '', $close_note = '')
1760 {
1761 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1762
1763 $this->db->begin();
1764
1765 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1766 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1767 if ($close_code) {
1768 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1769 }
1770 if ($close_note) {
1771 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1772 }
1773 $sql .= " WHERE rowid = ".((int) $this->id);
1774
1775 $resql = $this->db->query($sql);
1776 if ($resql) {
1777 // Bound discounts are deducted from the invoice
1778 // as they have not been used since the invoice is abandoned.
1779 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1780 $sql .= ' SET fk_invoice_supplier = NULL';
1781 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1782
1783 $resql = $this->db->query($sql);
1784 if ($resql) {
1785 // Call trigger
1786 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1787 if ($result < 0) {
1788 $this->db->rollback();
1789 return -1;
1790 }
1791 // End call triggers
1792
1793 $this->db->commit();
1794 return 1;
1795 } else {
1796 $this->error = $this->db->error()." sql=".$sql;
1797 $this->db->rollback();
1798 return -1;
1799 }
1800 } else {
1801 $this->error = $this->db->error()." sql=".$sql;
1802 $this->db->rollback();
1803 return -2;
1804 }
1805 }
1806
1816 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1817 {
1818 global $mysoc, $conf, $langs;
1819
1820 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1821
1822 $now = dol_now();
1823
1824 $error = 0;
1825 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1826
1827 // Force to have object complete for checks
1828 $this->fetch_thirdparty();
1829 $this->fetch_lines();
1830
1831 // Check parameters
1832 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1833 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1834 return 0;
1835 }
1836 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ', '/').'/', $this->ref_supplier)) {
1837 $langs->load("errors");
1838 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1839 return -1;
1840 }
1841 if (count($this->lines) <= 0) {
1842 $langs->load("errors");
1843 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1844 return -1;
1845 }
1846
1847 // Check for mandatory fields in thirdparty (defined into setup)
1848 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1849 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1850 foreach ($array_to_check as $key) {
1851 $keymin = strtolower($key);
1852 if ($keymin == 'accountancy_code_supplier') {
1853 $keymin = 'code_compta_fournisseur';
1854 }
1855 if (!property_exists($this->thirdparty, $keymin)) {
1856 continue;
1857 }
1858 $vallabel = $this->thirdparty->$keymin;
1859
1860 $i = (int) preg_replace('/[^0-9]/', '', $key);
1861 if ($i > 0) {
1862 if ($this->thirdparty->isACompany()) {
1863 // Check for mandatory prof id (but only if country is other than ours)
1864 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1865 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1866 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1867 $langs->load("errors");
1868 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1869 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1870 return -1;
1871 }
1872 }
1873 }
1874 } else {
1875 if ($key == 'EMAIL') {
1876 // Check for mandatory
1877 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1878 $langs->load("errors");
1879 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1880 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1881 return -1;
1882 }
1883 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1884 // Check for mandatory
1885 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1886 $langs->load("errors");
1887 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1888 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1889 return -1;
1890 }
1891 }
1892 }
1893 }
1894 }
1895
1896 $this->db->begin();
1897
1898 // Define new ref
1899 if ($force_number) {
1900 $num = $force_number;
1901 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1902 $num = $this->getNextNumRef($this->thirdparty);
1903 } else {
1904 $num = (string) $this->ref;
1905 }
1906 $this->newref = dol_sanitizeFileName($num);
1907
1908 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1909 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1910 $sql .= " WHERE rowid = ".((int) $this->id);
1911
1912 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1913 $resql = $this->db->query($sql);
1914 if ($resql) {
1915 // If we increment the main product and its components upon validation of the supplier invoice
1916 if (isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1917 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1918 $langs->load("agenda");
1919
1920 $cpt = count($this->lines);
1921 for ($i = 0; $i < $cpt; $i++) {
1922 if ($this->lines[$i]->fk_product > 0) {
1923 $mouvP = new MouvementStock($this->db);
1924 $mouvP->origin = &$this;
1925 $mouvP->setOrigin($this->element, $this->id);
1926 // We increase stock for product
1927 $up_ht_disc = $this->lines[$i]->subprice;
1928 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1929 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1930 }
1932 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1933 } else {
1934 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1935 }
1936 if ($result < 0) {
1937 $this->setErrorsFromObject($mouvP);
1938 return -2;
1939 }
1940 }
1941 }
1942 }
1943
1944 // Triggers call
1945 if (empty($notrigger)) {
1946 // Call trigger
1947 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1948 if ($result < 0) {
1949 $error++;
1950 }
1951 // End call triggers
1952 }
1953
1954 if (!$error) {
1955 $this->oldref = $this->ref;
1956
1957 // Rename directory if dir was a temporary ref
1958 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1959 // Now we rename also files into index
1960 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1961 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1962 $resql = $this->db->query($sql);
1963 if (!$resql) {
1964 $error++;
1965 $this->error = $this->db->lasterror();
1966 }
1967 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1968 $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1969 $resql = $this->db->query($sql);
1970 if (!$resql) {
1971 $error++;
1972 $this->error = $this->db->lasterror();
1973 }
1974
1975 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1976 $oldref = dol_sanitizeFileName($this->ref);
1977 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1978 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1979 if (!$error && file_exists($dirsource)) {
1980 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1981
1982 if (@rename($dirsource, $dirdest)) {
1983 dol_syslog("Rename ok");
1984 // Rename docs starting with $oldref with $this->newref
1985 $listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1986 foreach ($listoffiles as $fileentry) {
1987 $dirsource = $fileentry['name'];
1988 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1989 $dirsource = $fileentry['path'].'/'.$dirsource;
1990 $dirdest = $fileentry['path'].'/'.$dirdest;
1991 @rename($dirsource, $dirdest);
1992 }
1993 }
1994 }
1995 }
1996 }
1997
1998 // Set new ref and define current status
1999 if (!$error) {
2000 $this->ref = $this->newref;
2001 $this->statut = self::STATUS_VALIDATED;
2003 //$this->date_validation=$now; this is stored into log table
2004 }
2005
2006 if (!$error) {
2007 $this->db->commit();
2008 return 1;
2009 } else {
2010 $this->db->rollback();
2011 return -1;
2012 }
2013 } else {
2014 $this->error = $this->db->error();
2015 $this->db->rollback();
2016 return -1;
2017 }
2018 }
2019
2028 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
2029 {
2030 // phpcs:enable
2031 global $conf, $langs;
2032
2033 $error = 0;
2034
2035 if ($this->status == self::STATUS_DRAFT) {
2036 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
2037 return 0;
2038 }
2039
2040 dol_syslog(__METHOD__, LOG_DEBUG);
2041
2042 $this->db->begin();
2043
2044 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2045 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2046 $sql .= " WHERE rowid = ".((int) $this->id);
2047
2048 $result = $this->db->query($sql);
2049 if ($result) {
2050 $this->oldcopy = clone $this;
2051
2052 // If we increment the main product and its components upon supplier invoice validation, we decrement upon customer invoice validation
2053 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2054 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2055 $langs->load("agenda");
2056
2057 $cpt = count($this->lines);
2058 for ($i = 0; $i < $cpt; $i++) {
2059 if ($this->lines[$i]->fk_product > 0) {
2060 $mouvP = new MouvementStock($this->db);
2061 $mouvP->origin = &$this;
2062 $mouvP->setOrigin($this->element, $this->id);
2063 // We increase stock for product
2065 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2066 } else {
2067 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2068 }
2069 }
2070 }
2071 }
2072 // Triggers call
2073 if (empty($notrigger)) {
2074 // Call trigger
2075 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2076 if ($result < 0) {
2077 $error++;
2078 }
2079 // End call triggers
2080 }
2081 if ($error == 0) {
2082 $this->db->commit();
2083 return 1;
2084 } else {
2085 $this->db->rollback();
2086 return -1;
2087 }
2088 } else {
2089 $this->error = $this->db->error();
2090 $this->db->rollback();
2091 return -1;
2092 }
2093 }
2094
2095
2130 public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = 0, $date_end = 0, $fk_code_ventilation = 0, $info_bits = 0, $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = 0, $array_options = [], $fk_unit = null, $origin_id = 0, $pu_devise = 0, $ref_supplier = '', $special_code = 0, $fk_parent_line = 0, $fk_remise_except = 0, $origin_type = '')
2131 {
2132 global $langs, $mysoc;
2133
2134 dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$fk_code_ventilation,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG);
2135 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2136
2137 if ($this->status == self::STATUS_DRAFT) {
2138 // Clean parameters
2139 if (empty($remise_percent)) {
2140 $remise_percent = 0;
2141 }
2142 if (empty($qty)) {
2143 $qty = 0;
2144 }
2145 if (empty($info_bits)) {
2146 $info_bits = 0;
2147 }
2148 if (empty($rang)) {
2149 $rang = 0;
2150 }
2151 if (empty($fk_code_ventilation)) {
2152 $fk_code_ventilation = 0;
2153 }
2154 if (empty($txtva)) {
2155 $txtva = 0;
2156 }
2157 if (empty($txlocaltax1)) {
2158 $txlocaltax1 = 0;
2159 }
2160 if (empty($txlocaltax2)) {
2161 $txlocaltax2 = 0;
2162 }
2163
2164 $remise_percent = price2num($remise_percent);
2165 $qty = price2num($qty);
2166 $pu = price2num($pu);
2167 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2168 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2169 }
2170 $txlocaltax1 = price2num($txlocaltax1);
2171 $txlocaltax2 = price2num($txlocaltax2);
2172
2173 if ($date_start && $date_end && $date_start > $date_end) {
2174 $langs->load("errors");
2175 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2176 return -1;
2177 }
2178
2179 $this->db->begin();
2180
2181 if ($fk_product > 0) {
2182 if (getDolGlobalInt('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2183 // Check quantity is enough
2184 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2185 $prod = new ProductFournisseur($this->db);
2186 if ($prod->fetch($fk_product) > 0) {
2187 $product_type = $prod->type;
2188 $label = $prod->label;
2189 $fk_prod_fourn_price = 0;
2190
2191 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2192 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2193 $result = $prod->get_buyprice($fk_prod_fourn_price, (float) $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
2194 if ($result > 0) {
2195 if (empty($pu)) {
2196 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2197 }
2198 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2199 // is remise percent not keyed but present for the product we add it
2200 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2201 $remise_percent = $prod->remise_percent;
2202 }
2203 }
2204 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2205 $langs->load("errors");
2206 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2207 $this->db->rollback();
2208 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2209 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2210 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2211 return -1;
2212 }
2213 if ($result == -1) {
2214 $langs->load("errors");
2215 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2216 $this->db->rollback();
2217 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2218 return -1;
2219 }
2220 if ($result < -1) {
2221 $this->error = $prod->error;
2222 $this->db->rollback();
2223 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2224 return -1;
2225 }
2226 } else {
2227 $this->error = $prod->error;
2228 $this->db->rollback();
2229 return -1;
2230 }
2231 }
2232 } else {
2233 $product_type = $type;
2234 }
2235
2236 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2237 $pu = 0;
2238 }
2239
2240 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2241
2242 // Clean vat code
2243 $reg = array();
2244 $vat_src_code = '';
2245 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2246 $vat_src_code = $reg[1];
2247 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2248 }
2249
2250 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2251 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2252 // and this is done at the line level, which has its own VAT rate
2253
2254 $tabprice = calcul_price_total((float) $qty, $pu, (float) $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_devise);
2255 $total_ht = $tabprice[0];
2256 $total_tva = $tabprice[1];
2257 $total_ttc = $tabprice[2];
2258 $total_localtax1 = $tabprice[9];
2259 $total_localtax2 = $tabprice[10];
2260 $pu_ht = $tabprice[3];
2261
2262 // MultiCurrency
2263 $multicurrency_total_ht = $tabprice[16];
2264 $multicurrency_total_tva = $tabprice[17];
2265 $multicurrency_total_ttc = $tabprice[18];
2266 $pu_ht_devise = $tabprice[19];
2267
2268 // Check parameters
2269 if ($type < 0) {
2270 return -1;
2271 }
2272
2273 if ($rang < 0) {
2274 $rangmax = $this->line_max();
2275 $rang = $rangmax + 1;
2276 }
2277
2278 // Insert line
2279 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2280
2281 $supplierinvoiceline->context = $this->context;
2282
2283 $supplierinvoiceline->fk_facture_fourn = $this->id;
2284 //$supplierinvoiceline->label=$label; // deprecated
2285 $supplierinvoiceline->desc = $desc;
2286 $supplierinvoiceline->ref_supplier = $ref_supplier;
2287
2288 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2289 $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2290
2291 $supplierinvoiceline->vat_src_code = $vat_src_code;
2292 $supplierinvoiceline->tva_tx = $txtva;
2293 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2294 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2295 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2296 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2297
2298 $supplierinvoiceline->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht); // For credit note and if qty is negative, total is negative
2299 $supplierinvoiceline->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva); // For credit note and if qty is negative, total is negative
2300 $supplierinvoiceline->total_localtax1 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_localtax1) : (float) $total_localtax1); // For credit note and if qty is negative, total is negative
2301 $supplierinvoiceline->total_localtax2 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_localtax2) : (float) $total_localtax2); // For credit note and if qty is negative, total is negative
2302 $supplierinvoiceline->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc); // For credit note and if qty is negative, total is negative
2303
2304 $supplierinvoiceline->fk_product = $fk_product;
2305 $supplierinvoiceline->product_type = $type;
2306 $supplierinvoiceline->remise_percent = $remise_percent;
2307 $supplierinvoiceline->date_start = $date_start;
2308 $supplierinvoiceline->date_end = $date_end;
2309 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2310 $supplierinvoiceline->rang = $rang;
2311 $supplierinvoiceline->info_bits = $info_bits;
2312 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2313
2314
2315 $supplierinvoiceline->special_code = (int) $special_code;
2316 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2317 $supplierinvoiceline->origin = $this->origin;
2318 $supplierinvoiceline->origin_type = $origin_type;
2319 $supplierinvoiceline->origin_id = $origin_id;
2320 $supplierinvoiceline->fk_unit = $fk_unit;
2321
2322 // Multicurrency
2323 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2324 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2325 $supplierinvoiceline->multicurrency_subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs((float) $pu_ht_devise) : (float) $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2326
2327 $supplierinvoiceline->multicurrency_total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $multicurrency_total_ht) : (float) $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2328 $supplierinvoiceline->multicurrency_total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $multicurrency_total_tva) : (float) $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2329 $supplierinvoiceline->multicurrency_total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $multicurrency_total_ttc) : (float) $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2330
2331 if (is_array($array_options) && count($array_options) > 0) {
2332 $supplierinvoiceline->array_options = $array_options;
2333 }
2334
2335 $result = $supplierinvoiceline->insert($notrigger);
2336 if ($result > 0) {
2337 // Update denormalized fields at the order level
2338 $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // The addline method is designed to add line from user input so total calculation with update_price must be done using 'auto' mode.
2339
2340 if ($result > 0) {
2341 if (!isset($this->context['createfromclone'])) {
2342 if (!empty($fk_parent_line)) {
2343 // Always reorder if child line
2344 $this->line_order(true, 'DESC');
2345 } elseif ($rang > 0 && $rang <= count($this->lines)) {
2346 // Update all rank of all other lines starting from the same $ranktouse
2347 $linecount = count($this->lines);
2348 for ($ii = $rang; $ii <= $linecount; $ii++) {
2349 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2350 }
2351 }
2352
2353 $this->lines[] = $supplierinvoiceline;
2354 }
2355
2356 $this->db->commit();
2357 return $supplierinvoiceline->id;
2358 } else {
2359 $this->error = $this->db->error();
2360 $this->db->rollback();
2361 return -1;
2362 }
2363 } else {
2364 $this->error = $supplierinvoiceline->error;
2365 $this->errors = $supplierinvoiceline->errors;
2366 $this->db->rollback();
2367 return -2;
2368 }
2369 } else {
2370 return 0;
2371 }
2372 }
2373
2399 public function updateline($id, $desc, $pu, $vatrate, $txlocaltax1 = 0, $txlocaltax2 = 0, $qty = 1, $idproduct = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $remise_percent = 0, $notrigger = 0, $date_start = '', $date_end = '', $array_options = [], $fk_unit = null, $pu_devise = 0, $ref_supplier = '', $rang = 0)
2400 {
2401 global $mysoc, $langs;
2402
2403 dol_syslog(get_class($this)."::updateline $id,$desc,$pu,$vatrate,$qty,$idproduct,$price_base_type,$info_bits,$type,$remise_percent,$notrigger,$date_start,$date_end,$fk_unit,$pu_devise,$ref_supplier", LOG_DEBUG);
2404 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2405
2406 $pu = price2num($pu);
2407 $qty = price2num($qty);
2408 $remise_percent = (float) price2num($remise_percent);
2409 $pu_devise = price2num($pu_devise);
2410
2411 // Check parameters
2412 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2413 if ($type < 0) {
2414 return -1;
2415 }
2416
2417 if ($date_start && $date_end && $date_start > $date_end) {
2418 $langs->load("errors");
2419 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2420 return -1;
2421 }
2422
2423 // Clean parameters
2424 if (empty($vatrate)) {
2425 $vatrate = 0;
2426 }
2427 if (empty($txlocaltax1)) {
2428 $txlocaltax1 = 0;
2429 }
2430 if (empty($txlocaltax2)) {
2431 $txlocaltax2 = 0;
2432 }
2433
2434 $txlocaltax1 = (float) price2num($txlocaltax1);
2435 $txlocaltax2 = (float) price2num($txlocaltax2);
2436
2437 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2438 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2439 // and this is done at the line level, which has its own VAT rate
2440
2441 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2442
2443 $reg = array();
2444
2445 // Clean vat code
2446 $vat_src_code = '';
2447 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2448 $vat_src_code = $reg[1];
2449 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2450 }
2451
2452 $tabprice = calcul_price_total((float) $qty, (float) $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_devise);
2453 $total_ht = $tabprice[0];
2454 $total_tva = $tabprice[1];
2455 $total_ttc = $tabprice[2];
2456 $pu_ht = $tabprice[3];
2457 $pu_tva = $tabprice[4];
2458 $pu_ttc = $tabprice[5];
2459 $total_localtax1 = $tabprice[9];
2460 $total_localtax2 = $tabprice[10];
2461
2462 // MultiCurrency
2463 $multicurrency_total_ht = $tabprice[16];
2464 $multicurrency_total_tva = $tabprice[17];
2465 $multicurrency_total_ttc = $tabprice[18];
2466 $pu_ht_devise = $tabprice[19];
2467
2468 if (empty($info_bits)) {
2469 $info_bits = 0;
2470 }
2471
2472 // Fetch current line from the database and then clone the object and set it in $oldline property
2473 $line = new SupplierInvoiceLine($this->db);
2474 $line->fetch($id);
2475 $line->fetch_optionals();
2476
2477 $staticline = clone $line;
2478
2479 if ($idproduct) {
2480 $product = new Product($this->db);
2481 $result = $product->fetch($idproduct);
2482 $product_type = $product->type;
2483 } else {
2484 $idproduct = $staticline->fk_product;
2485 $product_type = $type;
2486 }
2487
2488 $line->oldline = $staticline;
2489 $line->context = $this->context;
2490
2491 $line->description = $desc;
2492 $line->desc = $desc;
2493
2494 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2495
2496 $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2497 $line->pu_ht = $line->subprice; // deprecated
2498 $line->subprice_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs((float) $pu_ttc) : (float) $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2499 $line->pu_ttc = $line->subprice_ttc; // deprecated
2500
2501 $line->remise_percent = $remise_percent;
2502 $line->ref_supplier = $ref_supplier;
2503
2504 $line->date_start = $date_start;
2505 $line->date_end = $date_end;
2506
2507 $line->vat_src_code = $vat_src_code;
2508 $line->tva_tx = $vatrate;
2509 $line->localtax1_tx = $txlocaltax1;
2510 $line->localtax2_tx = $txlocaltax2;
2511 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2512 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2513
2514 $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht);
2515 $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva);
2516 $line->total_localtax1 = (float) $total_localtax1;
2517 $line->total_localtax2 = (float) $total_localtax2;
2518 $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc);
2519
2520 $line->fk_product = $idproduct;
2521 $line->product_type = $product_type;
2522 $line->info_bits = $info_bits;
2523 $line->fk_unit = $fk_unit;
2524 $line->rang = $rang;
2525
2526 if (is_array($array_options) && count($array_options) > 0) {
2527 // We replace values in this->line->array_options only for entries defined into $array_options
2528 foreach ($array_options as $key => $value) {
2529 $line->array_options[$key] = $array_options[$key];
2530 }
2531 }
2532
2533 // Multicurrency
2534 $line->multicurrency_subprice = (float) $pu_ht_devise;
2535 $line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2536 $line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2537 $line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2538
2539 $res = $line->update($notrigger);
2540
2541 if ($res < 1) {
2542 $this->setErrorsFromObject($line);
2543 } else {
2544 // Update total price into invoice record
2545 $res = $this->update_price(1, 'auto', 0, $this->thirdparty);
2546 }
2547
2548 return $res;
2549 }
2550
2558 public function deleteLine($rowid, $notrigger = 0)
2559 {
2560 if (!$rowid) {
2561 $rowid = $this->id;
2562 }
2563
2564 $this->db->begin();
2565
2566 // Free the discount linked to a line of invoice
2567 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2568 $sql .= ' SET fk_invoice_supplier_line = NULL';
2569 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2570
2571 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2572 $result = $this->db->query($sql);
2573 if (!$result) {
2574 $this->error = $this->db->error();
2575 $this->db->rollback();
2576 return -2;
2577 }
2578
2579 $line = new SupplierInvoiceLine($this->db);
2580
2581 if ($line->fetch($rowid) < 1) {
2582 return -1;
2583 }
2584
2585 $res = $line->delete($notrigger);
2586
2587 if ($res < 1) {
2588 $this->errors[] = $line->error;
2589 $this->db->rollback();
2590 return -3;
2591 } else {
2592 $res = $this->update_price(1);
2593
2594 if ($res > 0) {
2595 $this->db->commit();
2596 return 1;
2597 } else {
2598 $this->db->rollback();
2599 $this->error = $this->db->lasterror();
2600 return -4;
2601 }
2602 }
2603 }
2604
2605
2612 public function info($id)
2613 {
2614 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2615 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2616 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2617 $sql .= ' WHERE c.rowid = '.((int) $id);
2618
2619 $result = $this->db->query($sql);
2620 if ($result) {
2621 if ($this->db->num_rows($result)) {
2622 $obj = $this->db->fetch_object($result);
2623
2624 $this->id = $obj->rowid;
2625
2626 $this->user_creation_id = $obj->fk_user_author;
2627 $this->user_validation_id = $obj->fk_user_valid;
2628 $this->user_modification_id = $obj->fk_user_modif;
2629 $this->date_creation = $this->db->jdate($obj->datec);
2630 $this->date_modification = $this->db->jdate($obj->datem);
2631 //$this->date_validation = $obj->datev; // This field is not available. Should be store into log table and using this function should be replaced with showing content of log (like for supplier orders)
2632 }
2633 $this->db->free($result);
2634 } else {
2635 dol_print_error($this->db);
2636 }
2637 }
2638
2639 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2648 public function list_replacable_supplier_invoices($socid = 0)
2649 {
2650 // phpcs:enable
2651 global $conf;
2652
2653 $return = array();
2654
2655 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2656 $sql .= " ff.rowid as rowidnext";
2657 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2658 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2659 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2660 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2661 $sql .= " AND f.entity = ".$conf->entity;
2662 $sql .= " AND f.paye = 0"; // Not closed completely
2663 $sql .= " AND pf.fk_paiementfourn IS NULL"; // No payment already done
2664 $sql .= " AND ff.fk_statut IS NULL"; // Return true (is null) if it is not a replacing invoice (we can't replace a replacing invoice)
2665 if ($socid > 0) {
2666 $sql .= " AND f.fk_soc = ".((int) $socid);
2667 }
2668 $sql .= " ORDER BY f.ref";
2669
2670 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2671 $resql = $this->db->query($sql);
2672 if ($resql) {
2673 while ($obj = $this->db->fetch_object($resql)) {
2674 $return[$obj->rowid] = array(
2675 'id' => $obj->rowid,
2676 'ref' => $obj->ref,
2677 'status' => $obj->fk_statut
2678 );
2679 }
2680 //print_r($return);
2681 return $return;
2682 } else {
2683 $this->error = $this->db->error();
2684 return -1;
2685 }
2686 }
2687
2688 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2697 public function list_qualified_avoir_supplier_invoices($socid = 0)
2698 {
2699 // phpcs:enable
2700 global $conf;
2701
2702 $return = array();
2703
2704 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye as paid, pf.fk_paiementfourn";
2705 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2706 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2707 $sql .= " WHERE f.entity = ".$conf->entity;
2708 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2709 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2710 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2711 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2712 if ($socid > 0) {
2713 $sql .= " AND f.fk_soc = ".((int) $socid);
2714 }
2715 $sql .= " ORDER BY f.ref";
2716
2717 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2718 $resql = $this->db->query($sql);
2719 if ($resql) {
2720 while ($obj = $this->db->fetch_object($resql)) {
2721 $qualified = 0;
2722 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2723 $qualified = 1;
2724 }
2725 if ($obj->fk_statut == self::STATUS_CLOSED) {
2726 $qualified = 1;
2727 }
2728 if ($qualified) {
2729 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2730 $return[$obj->rowid] = array('ref' => (string) $obj->ref, 'status' => (int) $obj->fk_statut, 'type' => (int) $obj->type, 'paid' => (int) $obj->paid, 'paye' => (int) $obj->paid, 'paymentornot' => (int) $paymentornot);
2731 }
2732 }
2733
2734 return $return;
2735 } else {
2736 $this->error = $this->db->error();
2737 return -1;
2738 }
2739 }
2740
2741 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2748 public function load_board($user)
2749 {
2750 // phpcs:enable
2751 global $conf, $langs;
2752
2753 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2754 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2755 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2756 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2757 }
2758 $sql .= ' WHERE ff.paye = 0';
2759 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2760 $sql .= " AND ff.entity = ".$conf->entity;
2761 if ($user->socid) {
2762 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2763 }
2764
2765 $resql = $this->db->query($sql);
2766 if ($resql) {
2767 $langs->load("bills");
2768 $now = dol_now();
2769
2770 $response = new WorkboardResponse();
2771 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2772 $response->label = $langs->trans("SupplierBillsToPay");
2773 $response->labelShort = $langs->trans("StatusToPay");
2774
2775 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2776 $response->img = img_object($langs->trans("Bills"), "bill");
2777
2778 $facturestatic = new FactureFournisseur($this->db);
2779
2780 while ($obj = $this->db->fetch_object($resql)) {
2781 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2782 $facturestatic->statut = $obj->status; // For backward compatibility
2783 $facturestatic->status = $obj->status;
2784
2785 $response->nbtodo++;
2786 $response->total += (float) $obj->total_ht;
2787
2788 if ($facturestatic->hasDelay()) {
2789 $response->nbtodolate++;
2790 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2791 }
2792 }
2793
2794 $this->db->free($resql);
2795 return $response;
2796 } else {
2797 dol_print_error($this->db);
2798 $this->error = $this->db->error();
2799 return -1;
2800 }
2801 }
2802
2810 public function getTooltipContentArray($params)
2811 {
2812 global $conf, $langs, $mysoc;
2813
2814 $langs->load('bills');
2815
2816 $datas = [];
2817 $moretitle = $params['moretitle'] ?? '';
2818
2819 $picto = $this->picto;
2820 if ($this->type == self::TYPE_REPLACEMENT) {
2821 $picto .= 'r'; // Replacement invoice
2822 }
2823 if ($this->type == self::TYPE_CREDIT_NOTE) {
2824 $picto .= 'a'; // Credit note
2825 }
2826 if ($this->type == self::TYPE_DEPOSIT) {
2827 $picto .= 'd'; // Deposit invoice
2828 }
2829
2830 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2831 if ($this->type == self::TYPE_REPLACEMENT) {
2832 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplacement").'</u>';
2833 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2834 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2835 } elseif ($this->type == self::TYPE_DEPOSIT) {
2836 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2837 }
2838 if (isset($this->status)) {
2839 $alreadypaid = -1;
2840 if (isset($this->totalpaid)) {
2841 $alreadypaid = $this->totalpaid;
2842 }
2843
2844 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2845 }
2846 if ($moretitle) {
2847 $datas['picto'] .= ' - '.$moretitle;
2848 }
2849 if (!empty($this->ref)) {
2850 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2851 }
2852 if (!empty($this->ref_supplier)) {
2853 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2854 }
2855 if (!empty($this->label)) {
2856 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2857 }
2858 if (!empty($this->date)) {
2859 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2860 }
2861 if (!empty($this->date_echeance)) {
2862 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2863 }
2864 if (!empty($this->total_ht)) {
2865 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2866 }
2867 if (!empty($this->total_tva)) {
2868 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2869 }
2870 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2871 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2872 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2873 }
2874 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2875 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2876 }
2877 if (!empty($this->revenuestamp)) {
2878 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2879 }
2880 if (!empty($this->total_ttc)) {
2881 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2882 }
2883 return $datas;
2884 }
2885
2899 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2900 {
2901 global $langs, $conf, $user, $hookmanager;
2902
2903 $result = '';
2904
2905 if ($option == 'withdraw') {
2906 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2907 } elseif ($option == 'document') {
2908 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2909 } else {
2910 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2911 }
2912
2913 if ($short) {
2914 return $url;
2915 }
2916
2917 if ($option !== 'nolink') {
2918 // Add param to save lastsearch_values or not
2919 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2920 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2921 $add_save_lastsearch_values = 1;
2922 }
2923 if ($add_save_lastsearch_values) {
2924 $url .= '&save_lastsearch_values=1';
2925 }
2926 }
2927
2928 $picto = $this->picto;
2929 if ($this->type == self::TYPE_REPLACEMENT) {
2930 $picto .= 'r'; // Replacement invoice
2931 }
2932 if ($this->type == self::TYPE_CREDIT_NOTE) {
2933 $picto .= 'a'; // Credit note
2934 }
2935 if ($this->type == self::TYPE_DEPOSIT) {
2936 $picto .= 'd'; // Deposit invoice
2937 }
2938
2939 $params = [
2940 'id' => $this->id,
2941 'objecttype' => $this->element,
2942 'option' => $option,
2943 'moretitle' => $moretitle,
2944 ];
2945 $classfortooltip = 'classfortooltip';
2946 $dataparams = '';
2947 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2948 $classfortooltip = 'classforajaxtooltip';
2949 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2950 $label = '';
2951 } else {
2952 $label = implode($this->getTooltipContentArray($params));
2953 }
2954
2955 $ref = $this->ref;
2956 if (empty($ref)) {
2957 $ref = $this->id;
2958 }
2959
2960 $linkclose = '';
2961 if (empty($notooltip)) {
2962 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2963 $label = $langs->trans("ShowSupplierInvoice");
2964 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2965 }
2966 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2967 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2968 }
2969
2970 $linkstart = '<a href="'.$url.'"';
2971 $linkstart .= $linkclose.'>';
2972 $linkend = '</a>';
2973
2974 $result .= $linkstart;
2975 if ($withpicto) {
2976 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2977 }
2978 if ($withpicto != 2) {
2979 $result .= ($max ? dol_trunc($ref, $max) : $ref);
2980 }
2981 $result .= $linkend;
2982
2983 if ($addlinktonotes) {
2984 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2985 if ($txttoshow) {
2986 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2987 $result .= ' <span class="note inline-block">';
2988 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2989 $result .= img_picto('', 'note');
2990 $result .= '</a>';
2991 $result .= '</span>';
2992 }
2993 }
2994 global $action;
2995 $hookmanager->initHooks(array($this->element . 'dao'));
2996 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2997 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2998 if ($reshook > 0) {
2999 $result = $hookmanager->resPrint;
3000 } else {
3001 $result .= $hookmanager->resPrint;
3002 }
3003 return $result;
3004 }
3005
3016 public function setCategories($categories)
3017 {
3018 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3019 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_INVOICE);
3020 }
3021
3030 public function getNextNumRef($soc, $mode = 'next')
3031 {
3032 global $db, $langs, $conf;
3033 $langs->load("orders");
3034
3035 // Clean parameters (if not defined or using deprecated value)
3036 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
3037 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
3038 }
3039
3040 $mybool = false;
3041
3042 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
3043 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
3044
3045 // Include file with class
3046 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3047
3048 foreach ($dirmodels as $reldir) {
3049 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
3050
3051 // Load file with numbering class (if found)
3052 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3053 }
3054
3055 if (!$mybool) {
3056 dol_print_error(null, "Failed to include file ".$file);
3057 return '';
3058 }
3059
3060 $obj = new $classname();
3061 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3062 $numref = "";
3063 $numref = $obj->getNextValue($soc, $this, $mode);
3064
3065 if ($numref != "") {
3066 return $numref;
3067 } else {
3068 $this->error = $obj->error;
3069 return -1;
3070 }
3071 }
3072
3073
3082 public function initAsSpecimen($option = '')
3083 {
3084 global $langs, $conf;
3085 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3086
3087 $now = dol_now();
3088
3089 // Load array of products prodids
3090 $num_prods = 0;
3091 $prodids = array();
3092
3093 $sql = "SELECT rowid";
3094 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3095 $sql .= " WHERE entity IN (".getEntity('product').")";
3096 $sql .= $this->db->plimit(100);
3097
3098 $resql = $this->db->query($sql);
3099 if ($resql) {
3100 $num_prods = $this->db->num_rows($resql);
3101 $i = 0;
3102 while ($i < $num_prods) {
3103 $i++;
3104 $row = $this->db->fetch_row($resql);
3105 $prodids[$i] = $row[0];
3106 }
3107 }
3108
3109 // Initialise parameters
3110 $this->id = 0;
3111 $this->ref = 'SPECIMEN';
3112 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3113 $this->specimen = 1;
3114 $this->socid = 1;
3115 $this->date = $now;
3116 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3117 $this->cond_reglement_code = 'RECEP';
3118 $this->mode_reglement_code = 'CHQ';
3119
3120 $this->note_public = 'This is a comment (public)';
3121 $this->note_private = 'This is a comment (private)';
3122
3123 $this->multicurrency_tx = 1;
3124 $this->multicurrency_code = $conf->currency;
3125
3126 $xnbp = 0;
3127 if (empty($option) || $option != 'nolines') {
3128 // Lines
3129 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
3130 while ($xnbp < $nbp) {
3131 $line = new SupplierInvoiceLine($this->db);
3132 $line->desc = $langs->trans("Description")." ".$xnbp;
3133 $line->qty = 1;
3134 $line->subprice = 100;
3135 $line->price = 100;
3136 $line->tva_tx = 19.6;
3137 $line->localtax1_tx = 0;
3138 $line->localtax2_tx = 0;
3139 if ($xnbp == 2) {
3140 $line->total_ht = 50;
3141 $line->total_ttc = 59.8;
3142 $line->total_tva = 9.8;
3143 $line->remise_percent = 50;
3144 } else {
3145 $line->total_ht = 100;
3146 $line->total_ttc = 119.6;
3147 $line->total_tva = 19.6;
3148 $line->remise_percent = 0;
3149 }
3150
3151 if ($num_prods > 0) {
3152 $prodid = mt_rand(1, $num_prods);
3153 $line->fk_product = $prodids[$prodid];
3154 }
3155 $line->product_type = 0;
3156
3157 $this->lines[$xnbp] = $line;
3158
3159 $this->total_ht += $line->total_ht;
3160 $this->total_tva += $line->total_tva;
3161 $this->total_ttc += $line->total_ttc;
3162
3163 $xnbp++;
3164 }
3165 }
3166
3167 $this->total_ht = $xnbp * 100;
3168 $this->total_tva = $xnbp * 19.6;
3169 $this->total_ttc = $xnbp * 119.6;
3170
3171 return 1;
3172 }
3173
3179 public function loadStateBoard()
3180 {
3181 global $conf, $user;
3182
3183 $this->nb = array();
3184
3185 $clause = "WHERE";
3186
3187 $sql = "SELECT count(f.rowid) as nb";
3188 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3189 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3190 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3191 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3192 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3193 $clause = "AND";
3194 }
3195 $sql .= " ".$clause." f.entity = ".((int) $conf->entity);
3196
3197 $resql = $this->db->query($sql);
3198 if ($resql) {
3199 while ($obj = $this->db->fetch_object($resql)) {
3200 $this->nb["supplier_invoices"] = $obj->nb;
3201 }
3202 $this->db->free($resql);
3203 return 1;
3204 } else {
3205 dol_print_error($this->db);
3206 $this->error = $this->db->error();
3207 return -1;
3208 }
3209 }
3210
3219 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3220 {
3221 global $conf, $langs, $hookmanager;
3222
3223 $error = 0;
3224
3225 $object = new FactureFournisseur($this->db);
3226
3227 $this->db->begin();
3228
3229 // Load source object
3230 $object->fetch($fromid);
3231 $objFrom = clone $object;
3232
3233 $object->id = 0;
3234 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3235 $object->status = self::STATUS_DRAFT;
3236
3237 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3238
3239 // Clear fields
3240 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3241 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3242 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3243 $object->fk_facture_source = 0;
3244 $object->date_creation = '';
3245 $object->date_validation = '';
3246 $object->date = (empty($this->date) ? dol_now() : $this->date);
3247 $object->ref_client = '';
3248 $object->close_code = '';
3249 $object->close_note = '';
3250 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3251 $object->note_private = '';
3252 $object->note_public = '';
3253 }
3254
3255 $object->date_echeance = $object->calculate_date_lim_reglement();
3256
3257 // Loop on each line of new invoice
3258 foreach ($object->lines as $i => $line) {
3259 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3260 unset($object->lines[$i]);
3261 }
3262 }
3263
3264 // Create clone
3265 $object->context['createfromclone'] = 'createfromclone';
3266 $result = $object->create($user);
3267
3268 // Other options
3269 if ($result < 0) {
3270 $this->error = $object->error;
3271 $this->errors = $object->errors;
3272 $error++;
3273 }
3274
3275 if (!$error) {
3276 // Hook of thirdparty module
3277 if (is_object($hookmanager)) {
3278 $parameters = array('objFrom' => $objFrom);
3279 $action = '';
3280 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
3281 if ($reshook < 0) {
3282 $this->setErrorsFromObject($hookmanager);
3283 $error++;
3284 }
3285 }
3286 }
3287
3288 unset($object->context['createfromclone']);
3289
3290 // End
3291 if (!$error) {
3292 $this->db->commit();
3293 return $object->id;
3294 } else {
3295 $this->db->rollback();
3296 return -1;
3297 }
3298 }
3299
3311 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3312 {
3313 global $langs;
3314
3315 $langs->load("suppliers");
3316 $outputlangs->load("products");
3317
3318 // Set the model on the model name to use
3319 if (empty($modele)) {
3320 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3321 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3322 } else {
3323 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3324 }
3325 } elseif ($modele == 'auto') {
3326 $modele = 'canelle';
3327 }
3328
3329 if (empty($modele)) {
3330 return 0;
3331 } else {
3332 $modelpath = "core/modules/supplier_invoice/doc/";
3333
3334 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3335 }
3336 }
3337
3342 public function getRights()
3343 {
3344 global $user;
3345
3346 return $user->hasRight("fournisseur", "facture");
3347 }
3348
3357 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3358 {
3359 $tables = array(
3360 'facture_fourn'
3361 );
3362
3363 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3364 }
3365
3374 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3375 {
3376 $tables = array(
3377 'facture_fourn_det'
3378 );
3379
3380 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3381 }
3382
3388 public function hasDelay()
3389 {
3390 global $conf;
3391
3392 $now = dol_now();
3393
3394 if (!$this->date_echeance) {
3395 return false;
3396 }
3397
3398 $status = isset($this->status) ? $this->status : $this->statut;
3399
3400 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3401 }
3402
3408 public function isCreditNoteUsed()
3409 {
3410 $isUsed = false;
3411
3412 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3413 $resql = $this->db->query($sql);
3414 if (!empty($resql)) {
3415 $obj = $this->db->fetch_object($resql);
3416 if (!empty($obj->fk_invoice_supplier)) {
3417 $isUsed = true;
3418 }
3419 }
3420
3421 return $isUsed;
3422 }
3430 public function getKanbanView($option = '', $arraydata = null)
3431 {
3432 global $langs;
3433
3434 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3435
3436 $picto = $this->picto;
3437 if ($this->type == self::TYPE_REPLACEMENT) {
3438 $picto .= 'r'; // Replacement invoice
3439 }
3440 if ($this->type == self::TYPE_CREDIT_NOTE) {
3441 $picto .= 'a'; // Credit note
3442 }
3443 if ($this->type == self::TYPE_DEPOSIT) {
3444 $picto .= 'd'; // Deposit invoice
3445 }
3446
3447 $return = '<div class="box-flex-item box-flex-grow-zero">';
3448 $return .= '<div class="info-box info-box-sm">';
3449 $return .= '<span class="info-box-icon bg-infobox-action">';
3450 $return .= img_picto('', $picto);
3451 $return .= '</span>';
3452 $return .= '<div class="info-box-content">';
3453 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3454 if ($selected >= 0) {
3455 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3456 }
3457 if (!empty($arraydata['thirdparty'])) {
3458 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3459 }
3460 if (!empty($this->date)) {
3461 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3462 }
3463 if (!empty($this->total_ht)) {
3464 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3465 $return .= ' '.$langs->trans("HT");
3466 $return .= '</span>';
3467 }
3468 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3469 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3470
3471 $return .= '</div>';
3472 $return .= '</div>';
3473 $return .= '</div>';
3474
3475 return $return;
3476 }
3477
3484 public function setVATReverseCharge($vatreversecharge)
3485 {
3486 if (!$this->table_element) {
3487 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3488 return -1;
3489 }
3490
3491 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3492
3493 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3494 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3495 $sql .= " WHERE rowid=".((int) $this->id);
3496
3497 if ($this->db->query($sql)) {
3498 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3499 return 1;
3500 } else {
3501 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3502 $this->error = $this->db->error();
3503 return 0;
3504 }
3505 }
3506
3518 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3519 {
3520 global $conf, $langs, $user;
3521
3522 $this->output = '';
3523 $this->error = '';
3524 $nbMailSend = 0;
3525
3526 $error = 0;
3527 $errorsMsg = array();
3528
3529 $langs->load('bills');
3530
3531 if (!isModEnabled(!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3532 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3533 return 0;
3534 }
3535 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3536 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3537 return 0;
3538 }
3539
3540 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3541 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3542 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3543 $formmail = new FormMail($this->db);
3544
3545 $now = dol_now();
3546 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3547
3548 $tmpinvoice = new FactureFournisseur($this->db);
3549
3550 dol_syslog(__METHOD__." start", LOG_INFO);
3551
3552 // Select all action comm reminder
3553 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3554 if (!empty($paymentmode) && $paymentmode != 'all') {
3555 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3556 }
3557 $sql .= " WHERE f.paye = 0"; // Only unpaid
3558 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3559 if ($datetouse == 'invoicedate') {
3560 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3561 } else {
3562 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3563 }
3564 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3565 if (!empty($paymentmode) && $paymentmode != 'all') {
3566 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3567 }
3568 // TODO Add a filter to check there is no payment started yet
3569 if ($datetouse == 'invoicedate') {
3570 $sql .= $this->db->order("datef", "ASC");
3571 } else {
3572 $sql .= $this->db->order("date_lim_reglement", "ASC");
3573 }
3574
3575 $resql = $this->db->query($sql);
3576
3577 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3578 if ($datetouse == 'invoicedate') {
3579 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3580 } else {
3581 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3582 }
3583 if (!empty($paymentmode) && $paymentmode != 'all') {
3584 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3585 }
3586 $this->output .= '<br>';
3587
3588 if ($resql) {
3589 while ($obj = $this->db->fetch_object($resql)) {
3590 if (!$error) {
3591 // Load event
3592 $res = $tmpinvoice->fetch($obj->id);
3593 if ($res > 0) {
3594 $tmpinvoice->fetch_thirdparty();
3595
3596 $outputlangs = new Translate('', $conf);
3597 if ($tmpinvoice->thirdparty->default_lang) {
3598 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3599 $outputlangs->loadLangs(array("main", "suppliers"));
3600 } else {
3601 $outputlangs = $langs;
3602 }
3603
3604 // Select email template according to language of recipient
3605 $templateId = 0;
3606 $templateLabel = '';
3607 if (empty($template) || $template == 'EmailTemplateCode') {
3608 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3609 } else {
3610 if (is_numeric($template)) {
3611 $templateId = $template;
3612 } else {
3613 $templateLabel = $template;
3614 }
3615 }
3616
3617 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3618 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3619 $langs->load("errors");
3620 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3621 return 0;
3622 }
3623
3624 // PREPARE EMAIL
3625 $errormesg = '';
3626
3627 // Make substitution in email content
3628 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $tmpinvoice);
3629
3630 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3631
3632 // Topic
3633 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3634
3635 // Content
3636 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3637
3638 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3639
3640 // Recipient
3641 $to = array();
3642 if ($forcerecipient) { // If a recipient was forced
3643 $to = array($forcerecipient);
3644 } else {
3645 $res = $tmpinvoice->fetch_thirdparty();
3646 $recipient = $tmpinvoice->thirdparty;
3647 if ($res > 0) {
3648 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3649 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3650 foreach ($tmparraycontact as $data_email) {
3651 if (!empty($data_email['email'])) {
3652 $to[] = $data_email['email'];
3653 }
3654 }
3655 }
3656 if (empty($to) && !empty($recipient->email)) {
3657 $to[] = $recipient->email;
3658 }
3659 if (empty($to)) {
3660 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3661 $error++;
3662 }
3663 } else {
3664 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3665 $error++;
3666 }
3667 }
3668
3669 // Sender
3670 $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3671 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3672 $from = (string) $arraymessage->email_from;
3673 }
3674 if (empty($from)) {
3675 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3676 $error++;
3677 }
3678
3679 if (!$error && !empty($to)) {
3680 $this->db->begin();
3681
3682 $to = implode(',', $to);
3683 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3684 $to = $to.','.$arraymessage->email_to;
3685 }
3686
3687 // Errors Recipient
3688 $errors_to = getDolGlobalString('MAIN_MAIL_ERRORS_TO');
3689
3690 $trackid = 'inv'.$tmpinvoice->id;
3691 $sendcontext = 'standard';
3692
3693 $email_tocc = '';
3694 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3695 $email_tocc = (string) $arraymessage->email_tocc;
3696 }
3697
3698 $email_tobcc = '';
3699 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3700 $email_tobcc = (string) $arraymessage->email_tobcc;
3701 }
3702
3703 // Mail Creation
3704 $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3705
3706 // Sending Mail
3707 if ($cMailFile->sendfile()) {
3708 $nbMailSend++;
3709
3710 // Add a line into event table
3711 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3712
3713 // Insert record of emails sent
3714 $actioncomm = new ActionComm($this->db);
3715
3716 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3717 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3718 $actioncomm->contact_id = 0;
3719
3720 $actioncomm->code = 'AC_EMAIL';
3721 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3722 $actioncomm->note_private = $sendContent;
3723 $actioncomm->fk_project = $tmpinvoice->fk_project;
3724 $actioncomm->datep = dol_now();
3725 $actioncomm->datef = $actioncomm->datep;
3726 $actioncomm->percentage = -1; // Not applicable
3727 $actioncomm->authorid = $user->id; // User saving action
3728 $actioncomm->userownerid = $user->id; // Owner of action
3729 // Fields when action is an email (content should be added into note)
3730 $actioncomm->email_msgid = $cMailFile->msgid;
3731 $actioncomm->email_subject = $sendTopic;
3732 $actioncomm->email_from = $from;
3733 $actioncomm->email_sender = '';
3734 $actioncomm->email_to = $to;
3735 //$actioncomm->email_tocc = $sendtocc;
3736 //$actioncomm->email_tobcc = $sendtobcc;
3737 //$actioncomm->email_subject = $subject;
3738 $actioncomm->errors_to = $errors_to;
3739
3740 $actioncomm->elementtype = 'invoice_supplier';
3741 $actioncomm->elementid = $tmpinvoice->id;
3742 $actioncomm->fk_element = $tmpinvoice->id;
3743
3744 //$actioncomm->extraparams = $extraparams;
3745
3746 $actioncomm->create($user);
3747 } else {
3748 $errormesg = $cMailFile->error.' : '.$to;
3749 $error++;
3750
3751 // Add a line into event table
3752 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3753
3754 // Insert record of emails sent
3755 $actioncomm = new ActionComm($this->db);
3756
3757 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3758 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3759 $actioncomm->contact_id = 0;
3760
3761 $actioncomm->code = 'AC_EMAIL';
3762 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3763 $actioncomm->note_private = $errormesg;
3764 $actioncomm->fk_project = $tmpinvoice->fk_project;
3765 $actioncomm->datep = dol_now();
3766 $actioncomm->datef = $actioncomm->datep;
3767 $actioncomm->percentage = -1; // Not applicable
3768 $actioncomm->authorid = $user->id; // User saving action
3769 $actioncomm->userownerid = $user->id; // Owner of action
3770 // Fields when action is an email (content should be added into note)
3771 $actioncomm->email_msgid = $cMailFile->msgid;
3772 $actioncomm->email_from = $from;
3773 $actioncomm->email_sender = '';
3774 $actioncomm->email_to = $to;
3775 //$actioncomm->email_tocc = $sendtocc;
3776 //$actioncomm->email_tobcc = $sendtobcc;
3777 //$actioncomm->email_subject = $subject;
3778 $actioncomm->errors_to = $errors_to;
3779
3780 //$actioncomm->extraparams = $extraparams;
3781
3782 $actioncomm->create($user);
3783 }
3784
3785 $this->db->commit(); // We always commit
3786 }
3787
3788 if ($errormesg) {
3789 $errorsMsg[] = $errormesg;
3790 }
3791 } else {
3792 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3793 $error++;
3794 }
3795 }
3796 }
3797 } else {
3798 $error++;
3799 }
3800
3801 if (!$error) {
3802 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3803
3804 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3805
3806 return 0;
3807 } else {
3808 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3809
3810 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3811
3812 return $error;
3813 }
3814 }
3815}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Superclass for invoice classes.
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class to manage suppliers invoices.
const TYPE_DEPOSIT
Deposit invoice.
create($user)
Create supplier invoice into database.
list_qualified_avoir_supplier_invoices($socid=0)
Return list of qualifying invoices for correction by credit note Invoices that respect the following ...
list_replacable_supplier_invoices($socid=0)
Return list of replaceable invoices Status valid or abandoned for other reason + not paid + no paymen...
deleteLine($rowid, $notrigger=0)
Delete a detail line from database.
set_unpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
setCanceled($user, $close_code='', $close_note='')
Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never rece...
fetch($id=0, $ref='', $ref_ext='')
Load object in memory from database.
setCategories($categories)
Sets object to given categories.
info($id)
Loads the info order information into the invoice object.
const TYPE_CREDIT_NOTE
Credit note invoice.
isCreditNoteUsed()
Is credit note used.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable name (with optional picto)
getTooltipContentArray($params)
getTooltipContentArray
setPaid($user, $close_code='', $close_note='')
Tag invoice as a paid invoice.
update($user=null, $notrigger=0)
Update database.
addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product=0, $remise_percent=0, $date_start=0, $date_end=0, $fk_code_ventilation=0, $info_bits=0, $price_base_type='HT', $type=0, $rang=-1, $notrigger=0, $array_options=[], $fk_unit=null, $origin_id=0, $pu_devise=0, $ref_supplier='', $special_code=0, $fk_parent_line=0, $fk_remise_except=0, $origin_type='')
Adds an invoice line (associated with no predefined product/service) The parameters are already suppo...
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
const TYPE_REPLACEMENT
Replacement invoice.
setUnpaid($user)
Tag the invoice as not fully paid + trigger call BILL_UNPAYED Function used when a direct debit payme...
const STATUS_VALIDATED
Validated (need to be paid)
setDraft($user, $idwarehouse=-1, $notrigger=0)
Set draft status.
getNextNumRef($soc, $mode='next')
Return next reference of supplier invoice not already used (or last reference) according to numbering...
updateline($id, $desc, $pu, $vatrate, $txlocaltax1=0, $txlocaltax2=0, $qty=1, $idproduct=0, $price_base_type='HT', $info_bits=0, $type=0, $remise_percent=0, $notrigger=0, $date_start='', $date_end='', $array_options=[], $fk_unit=null, $pu_devise=0, $ref_supplier='', $rang=0)
Update a line detail in the database.
sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays=0, $paymentmode='all', $template='', $datetouse='duedate', $forcerecipient='')
Send reminders by emails for supplier invoices validated that are due.
insert_discount($idremise)
Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume...
initAsSpecimen($option='')
Initialise an instance with random values.
const TYPE_STANDARD
Standard invoice.
validate($user, $force_number='', $idwarehouse=0, $notrigger=0)
Tag invoice as validated + call trigger BILL_VALIDATE.
set_paid($user, $close_code='', $close_note='')
Tag invoice as a paid invoice.
createFromClone(User $user, $fromid, $invertdetail=0)
Load an object from its id and create a new one in database.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template model.
getRights()
Returns the rights used for this class.
const STATUS_ABANDONED
Classified abandoned and no payment done.
hasDelay()
Is the payment of the supplier invoice having a delay?
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
loadStateBoard()
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
const STATUS_CLOSED
Classified paid.
setVATReverseCharge($vatreversecharge)
Change the option VAT reverse charge.
Class to manage invoice templates.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class to manage "other" html components Only common components are here.
Class to manage stock movements.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
static getIdFromCode($dbs, $code)
Get id of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage line invoices.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:168
global $mysoc
dol_get_first_hour($date, $gm='tzserver')
Return GMT time for first hour of a given GMT date (it removes hours, min and second part)
Definition date.lib.php:664
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:126
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:487
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130