dolibarr 24.0.1
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
191 public $date_pointoftax;
192
197 public $amount = 0;
202 public $remise = 0;
203
208 public $tva;
209
210 // Warning: Do not set default value into property definition. it must stay null.
211 // For example to avoid to have substitution done when object is generic and not yet defined.
213 public $localtax1;
215 public $localtax2;
217 public $total_ht;
219 public $total_tva;
221 public $total_localtax1;
223 public $total_localtax2;
225 public $total_ttc;
226
232 public $note;
234 public $note_private;
236 public $note_public;
238 public $propalid;
239
243 public $fk_account; // default bank account
244
248 public $transport_mode_id;
249
253 public $vat_reverse_charge;
254
258 public $extraparams = array();
259
263 public $lines = array();
264
269 public $fournisseur;
270
272
275 public $fk_facture_source;
276
278 public $fac_rec;
280 public $fk_fac_rec_source;
281
282 public $fields = array(
283 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
284 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
285 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
286 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
287 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
288 'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
289 'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
290 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
291 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
292 'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
293 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
294 'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
295 'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
296 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
297 'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
298 'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
299 'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
300 'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
301 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
302 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
303 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
304 'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
305 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
306 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
307 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
308 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
309 'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
310 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
311 'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
312 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
313 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
314 'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
315 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
316 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
317 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
318 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
319 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
320 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
321 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
322 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
323 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
324 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
325 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
326 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
327 'date_pointoftax' => array('type' => 'date', 'label' => 'DatePointOfTax', 'enabled' => 'getDolGlobalString("INVOICE_POINTOFTAX_DATE")', 'visible' => -1, 'position' => 235),
328 'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
329 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
330 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
331 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
332 );
333
334
338 const TYPE_STANDARD = 0;
339
344
349
353 const TYPE_DEPOSIT = 3;
354
358 const STATUS_DRAFT = 0;
359
364
372 const STATUS_CLOSED = 2;
373
382
383 const CLOSECODE_DISCOUNTVAT = 'discount_vat';
384 const CLOSECODE_BADCREDIT = 'badsupplier';
385 const CLOSECODE_ABANDONED = 'abandon';
386 const CLOSECODE_REPLACED = 'replaced';
387
388
394 public function __construct($db)
395 {
396 $this->db = $db;
397
398 $this->ismultientitymanaged = 1;
399 }
400
407 public function create($user)
408 {
409 global $langs, $conf, $hookmanager;
410
411 $error = 0;
412 $now = dol_now();
413
414 // Clean parameters
415 if (isset($this->ref_supplier)) {
416 $this->ref_supplier = trim($this->ref_supplier);
417 }
418 if (empty($this->type)) {
419 $this->type = self::TYPE_STANDARD;
420 }
421 if (empty($this->date)) {
422 $this->date = $now;
423 }
424
425 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
426 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
427 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
428 } else {
429 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
430 }
431 if (empty($this->fk_multicurrency)) {
432 $this->multicurrency_code = $conf->currency;
433 $this->fk_multicurrency = 0;
434 $this->multicurrency_tx = 1;
435 }
436 $this->entity = setEntity($this);
437
438 $this->db->begin();
439
440 // Defaults
441 $originaldatewhen = 0;
442 $nextdatewhen = 0;
443 $previousdaynextdatewhen = 0;
444 $_facrec = null;
445
446 // Create invoice from a template recurring invoice
447 if ($this->fac_rec > 0) {
448 $this->fk_fac_rec_source = $this->fac_rec;
449
450 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
451 $_facrec = new FactureFournisseurRec($this->db);
452 $result = $_facrec->fetch($this->fac_rec);
453 $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
454
455 // Define some dates
456 if (!empty($_facrec->frequency)) {
457 $originaldatewhen = $_facrec->date_when;
458 $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
459 $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
460 $this->socid = $_facrec->socid;
461 }
462
463 $this->entity = $_facrec->entity; // Invoice created in same entity than template
464
465 // Fields coming from GUI
466 // @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
467 // set by posted page with $object->xxx = ... and this section should be removed.
468 $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
469 $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
470 $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
471 $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
472 $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
473 $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
474 $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
475
476 // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
477 $this->total_ht = $_facrec->total_ht;
478 $this->total_ttc = $_facrec->total_ttc;
479
480 // Fields always coming from template
481 $this->fk_incoterms = $_facrec->fk_incoterms;
482 $this->location_incoterms = $_facrec->location_incoterms;
483
484 // Clean parameters
485 if (! $this->type) {
486 $this->type = self::TYPE_STANDARD;
487 }
488 $this->note_public = trim((string) $this->note_public);
489 $this->note_private = trim((string) $this->note_private);
490 $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
491
492 $this->array_options = $_facrec->array_options;
493
494 if (! $this->mode_reglement_id) {
495 $this->mode_reglement_id = 0;
496 }
497 $this->status = self::STATUS_DRAFT;
498 $this->statut = self::STATUS_DRAFT; // deprecated
499
500 $this->linked_objects = $_facrec->linkedObjectsIds;
501 // We do not add link to template invoice or next invoice will be linked to all generated invoices
502 //$this->linked_objects['facturerec'][0] = $this->fac_rec;
503
504 $forceduedate = $this->calculate_date_lim_reglement();
505
506 // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
507 if ($_facrec->frequency > 0) {
508 $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
509 dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
510 if (empty($_facrec->date_when)) {
511 $_facrec->date_when = $now;
512 }
513 $next_date = $_facrec->getNextDate(); // Calculate next date
514 $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
515 //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
516 $result = $_facrec->setNextDate($next_date, 1);
517 }
518
519 // Define lang of customer
520 $outputlangs = $langs;
521 $newlang = '';
522
523 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
524 $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
525 }
526 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && property_exists($this, 'default_lang') && isset($this->default_lang)) { // @phan-suppress-current-line PhanUndeclaredProperty
527 $newlang = $this->default_lang; // for thirdparty @phan-suppress-current-line PhanUndeclaredProperty
528 }
529 if (!empty($newlang)) {
530 $outputlangs = new Translate("", $conf);
531 $outputlangs->setDefaultLang($newlang);
532 } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
533 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
534 $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
535 $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
536 $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
537 $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
538 $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
539 $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
540 $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
541 $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
542 $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
543 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
544 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
545 $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
546 $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
547 $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
548
549 complete_substitutions_array($substitutionarray, $outputlangs);
550
551 $this->note_public = make_substitutions($this->note_public, $substitutionarray);
552 $this->note_private = make_substitutions($this->note_private, $substitutionarray);
553 }
554
555 // Define due date if not already defined
556 if (!empty($forceduedate)) {
557 $this->date_echeance = $forceduedate;
558 }
559
560 $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
561 $sql .= "ref";
562 $sql .= ", ref_supplier";
563 $sql .= ", ref_ext";
564 $sql .= ", entity";
565 $sql .= ", type";
566 $sql .= ", subtype";
567 $sql .= ", libelle";
568 $sql .= ", fk_soc";
569 $sql .= ", datec";
570 $sql .= ", datef";
571 $sql .= ", date_pointoftax";
572 $sql .= ", vat_reverse_charge";
573 $sql .= ", fk_projet";
574 $sql .= ", fk_cond_reglement";
575 $sql .= ", fk_mode_reglement";
576 $sql .= ", fk_account";
577 $sql .= ", note_private";
578 $sql .= ", note_public";
579 $sql .= ", fk_user_author";
580 $sql .= ", date_lim_reglement";
581 $sql .= ", fk_incoterms, location_incoterms";
582 $sql .= ", fk_multicurrency";
583 $sql .= ", multicurrency_code";
584 $sql .= ", multicurrency_tx";
585 $sql .= ", fk_facture_source";
586 $sql .= ", fk_fac_rec_source";
587 $sql .= ")";
588 $sql .= " VALUES (";
589 $sql .= "'(PROV)'";
590 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
591 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
592 $sql .= ", ".((int) $this->entity);
593 $sql .= ", '".$this->db->escape((string) $this->type)."'";
594 $sql .= ", ".(isset($this->subtype) ? (int) $this->subtype : "null");
595 $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
596 $sql .= ", ".((int) $this->socid);
597 $sql .= ", '".$this->db->idate($now)."'";
598 $sql .= ", '".$this->db->idate($this->date)."'";
599 $sql .= ", ".(empty($this->date_pointoftax) ? "null" : "'".$this->db->idate($this->date_pointoftax)."'");
600 $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0);
601 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
602 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
603 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
604 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
605 $sql .= ", '".$this->db->escape($this->note_private)."'";
606 $sql .= ", '".$this->db->escape($this->note_public)."'";
607 $sql .= ", ".((int) $user->id).",";
608 $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
609 $sql .= ", ".(int) $this->fk_incoterms;
610 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
611 $sql .= ", ".(int) $this->fk_multicurrency;
612 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
613 $sql .= ", ".(float) $this->multicurrency_tx;
614 $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
615 $sql .= ", ".(isset($this->fk_fac_rec_source) ? ((int) $this->fk_fac_rec_source) : "NULL");
616 $sql .= ")";
617
618 dol_syslog(get_class($this)."::create", LOG_DEBUG);
619 $resql = $this->db->query($sql);
620 if ($resql) {
621 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
622
623 // Update ref with new one
624 $this->ref = '(PROV'.$this->id.')';
625 $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
626
627 dol_syslog(get_class($this)."::create", LOG_DEBUG);
628 $resql = $this->db->query($sql);
629 if (!$resql) {
630 $error++;
631 }
632
633 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
634 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
635 }
636
637 // Add object linked
638 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
639 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
640 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, ...))
641 foreach ($tmp_origin_id as $origin_id) {
642 $ret = $this->add_object_linked($origin, $origin_id);
643 if (!$ret) {
644 dol_print_error($this->db);
645 $error++;
646 }
647 }
648 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
649 $origin_id = $tmp_origin_id;
650 $ret = $this->add_object_linked($origin, $origin_id);
651 if (!$ret) {
652 dol_print_error($this->db);
653 $error++;
654 }
655 }
656 }
657 }
658
659 if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
660 dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
661 foreach ($this->lines as $i => $val) {
662 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
663 $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').')';
664
665 $resql_insert = $this->db->query($sql);
666 if ($resql_insert) {
667 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
668
669 $res = $this->updateline(
670 $idligne,
671 $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
672 $this->lines[$i]->subprice,
673 $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
674 $this->lines[$i]->localtax1_tx,
675 $this->lines[$i]->localtax2_tx,
676 $this->lines[$i]->qty,
677 $this->lines[$i]->fk_product,
678 'HT',
679 (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
680 $this->lines[$i]->product_type,
681 $this->lines[$i]->remise_percent,
682 0,
683 $this->lines[$i]->date_start,
684 $this->lines[$i]->date_end,
685 $this->lines[$i]->array_options,
686 $this->lines[$i]->fk_unit,
687 $this->lines[$i]->multicurrency_subprice,
688 $this->lines[$i]->ref_supplier
689 );
690 } else {
691 $this->error = $this->db->lasterror();
692 $this->db->rollback();
693 return -5;
694 }
695 }
696 } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
697 dol_syslog("There is ".count($this->lines)." lines that are array lines");
698 foreach ($this->lines as $i => $val) {
699 $line = $this->lines[$i];
700
701 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
702 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
703 if (!is_object($line)) {
704 $line = (object) $line;
705 }
706
707 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
708 $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').')';
709
710 $resql_insert = $this->db->query($sql);
711 if ($resql_insert) {
712 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
713
714 $this->updateline(
715 $idligne,
716 $line->desc ? $line->desc : $line->description,
717 $line->subprice,
718 $line->tva_tx,
719 $line->localtax1_tx,
720 $line->localtax2_tx,
721 $line->qty,
722 $line->fk_product,
723 'HT',
724 (!empty($line->info_bits) ? $line->info_bits : ''),
725 $line->product_type,
726 $line->remise_percent,
727 0,
728 $line->date_start,
729 $line->date_end,
730 $line->array_options,
731 $line->fk_unit,
732 $line->multicurrency_subprice,
733 $line->ref_supplier
734 );
735 } else {
736 $this->error = $this->db->lasterror();
737 $this->db->rollback();
738 return -5;
739 }
740 }
741 }
742
743 /*
744 * Insert lines of template invoices
745 */
746 if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
747 foreach ($_facrec->lines as $i => $val) {
748 $product_type = $_facrec->lines[$i]->product_type;
749 if ($_facrec->lines[$i]->fk_product) {
750 $prod = new Product($this->db);
751 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
752 }
753
754 // For line from template invoice, we use data from template invoice
755 /*
756 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
757 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
758 if (empty($tva_tx)) $tva_npr=0;
759 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
760 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
761 */
762 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
763 $tva_npr = $_facrec->lines[$i]->info_bits;
764 if (empty($tva_tx)) {
765 $tva_npr = 0;
766 }
767 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
768 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
769
770 // $fk_product_fournisseur_price not used and does not exist on line
771 // $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
772 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
773
774 // If buyprice not defined from template invoice, we try to guess the best value
775 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
776 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
777 $producttmp = new ProductFournisseur($this->db);
778 $producttmp->fetch($_facrec->lines[$i]->fk_product);
779
780 // If margin module defined on costprice, we try the costprice
781 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
782 // else we get the best supplier price
783 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
784 $buyprice = $producttmp->cost_price;
785 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
786 $buyprice = $producttmp->pmp;
787 } else {
788 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
789 if ($producttmp->product_fourn_price_id > 0) {
790 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
791 }
792 }
793 }
794 }
795
796 $result_insert = $this->addline(
797 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
798 $_facrec->lines[$i]->pu_ht,
799 $tva_tx,
800 $localtax1_tx,
801 $localtax2_tx,
802 $_facrec->lines[$i]->qty,
803 $_facrec->lines[$i]->fk_product,
804 $_facrec->lines[$i]->remise_percent,
805 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
806 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
807 0,
808 $_facrec->lines[$i]->info_bits,
809 'HT',
810 $product_type,
811 $_facrec->lines[$i]->rang,
812 0,
813 $_facrec->lines[$i]->array_options,
814 $_facrec->lines[$i]->fk_unit,
815 0,
816 0,
817 $_facrec->lines[$i]->ref_supplier,
818 $_facrec->lines[$i]->special_code,
819 0,
820 0
821 );
822 if ($result_insert < 0) {
823 $error++;
824 $this->error = $this->db->error();
825 break;
826 }
827 }
828 }
829
830
831 // Update total price
832 $result = $this->update_price(1);
833 if ($result > 0) {
834 // Actions on extra fields
835 if (!$error) {
836 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
837 if ($result < 0) {
838 $error++;
839 }
840 }
841
842 if (!$error) {
843 // Call trigger
844 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
845 if ($result < 0) {
846 $error++;
847 }
848 // End call triggers
849 }
850
851 if (!$error) {
852 $this->db->commit();
853 return $this->id;
854 } else {
855 $this->db->rollback();
856 return -4;
857 }
858 } else {
859 $this->error = $langs->trans('FailedToUpdatePrice');
860 $this->db->rollback();
861 return -3;
862 }
863 } else {
864 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
865 $this->error = $langs->trans('ErrorRefAlreadyExists');
866 $this->db->rollback();
867 return -1;
868 } else {
869 $this->error = $this->db->lasterror();
870 $this->db->rollback();
871 return -2;
872 }
873 }
874 }
875
884 public function fetch($id = 0, $ref = '', $ref_ext = '')
885 {
886 if (empty($id) && empty($ref) && empty($ref_ext)) {
887 return -1;
888 }
889
890 $sql = "SELECT";
891 $sql .= " t.rowid,";
892 $sql .= " t.ref,";
893 $sql .= " t.ref_supplier,";
894 $sql .= " t.ref_ext,";
895 $sql .= " t.entity,";
896 $sql .= " t.type,";
897 $sql .= " t.subtype,";
898 $sql .= " t.fk_soc,";
899 $sql .= " t.datec,";
900 $sql .= " t.datef,";
901 $sql .= " t.date_pointoftax,";
902 $sql .= " t.tms as datem,";
903 $sql .= " t.libelle as label,";
904 $sql .= " t.paye as paid,";
905 $sql .= " t.close_code,";
906 $sql .= " t.close_note,";
907 $sql .= " t.tva,";
908 $sql .= " t.localtax1,";
909 $sql .= " t.localtax2,";
910 $sql .= " t.total_ht,";
911 $sql .= " t.total_tva,";
912 $sql .= " t.total_ttc,";
913 $sql .= " t.fk_statut as status,";
914 $sql .= " t.fk_user_author,";
915 $sql .= " t.fk_user_valid,";
916 $sql .= " t.fk_facture_source,";
917 $sql .= " t.vat_reverse_charge,";
918 $sql .= " t.fk_fac_rec_source,";
919 $sql .= " t.fk_projet as fk_project,";
920 $sql .= " t.fk_cond_reglement,";
921 $sql .= " t.fk_account,";
922 $sql .= " t.fk_mode_reglement,";
923 $sql .= " t.date_lim_reglement,";
924 $sql .= " t.note_private,";
925 $sql .= " t.note_public,";
926 $sql .= " t.model_pdf,";
927 $sql .= " t.last_main_doc,";
928 $sql .= " t.import_key,";
929 $sql .= " t.extraparams,";
930 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
931 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
932 $sql .= ' s.nom as socnom, s.rowid as socid,';
933 $sql .= ' t.fk_incoterms, t.location_incoterms,';
934 $sql .= " i.libelle as label_incoterms,";
935 $sql .= ' t.fk_transport_mode,';
936 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
937 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
938 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
939 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
940 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
941 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
942 if ($id) {
943 $sql .= " WHERE t.rowid = ".((int) $id);
944 } else {
945 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
946 if ($ref) {
947 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
948 }
949 if ($ref_ext) {
950 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
951 }
952 }
953
954 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
955 $resql = $this->db->query($sql);
956 if ($resql) {
957 if ($this->db->num_rows($resql)) {
958 $obj = $this->db->fetch_object($resql);
959
960 $this->id = $obj->rowid;
961 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
962
963 $this->ref_supplier = $obj->ref_supplier;
964 $this->ref_ext = $obj->ref_ext;
965 $this->entity = $obj->entity;
966 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
967 $this->subtype = $obj->subtype;
968 $this->socid = $obj->fk_soc;
969 $this->date = $this->db->jdate($obj->datef);
970 $this->date_pointoftax = $this->db->jdate($obj->date_pointoftax);
971 $this->date_creation = $this->db->jdate($obj->datec);
972 $this->datec = $this->db->jdate($obj->datec);
973 $this->date_modification = $this->db->jdate($obj->datem);
974 $this->tms = $this->db->jdate($obj->datem);
975 $this->libelle = $obj->label; // deprecated
976 $this->label = $obj->label;
977 $this->paye = $obj->paid;
978 $this->paid = $obj->paid;
979 $this->close_code = $obj->close_code;
980 $this->close_note = $obj->close_note;
981 $this->total_localtax1 = $obj->localtax1;
982 $this->total_localtax2 = $obj->localtax2;
983 $this->total_ht = $obj->total_ht;
984 $this->total_tva = $obj->total_tva;
985 $this->total_ttc = $obj->total_ttc;
986 $this->status = $obj->status;
987 $this->statut = $obj->status; // For backward compatibility
988 $this->fk_statut = $obj->status; // For backward compatibility
989 $this->user_creation_id = $obj->fk_user_author;
990 $this->author = $obj->fk_user_author; // deprecated
991 $this->user_validation_id = $obj->fk_user_valid;
992 $this->fk_user_valid = $obj->fk_user_valid;
993 $this->fk_facture_source = $obj->fk_facture_source;
994 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
995 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
996 $this->fk_project = $obj->fk_project;
997 $this->cond_reglement_id = $obj->fk_cond_reglement;
998 $this->cond_reglement_code = $obj->cond_reglement_code;
999 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
1000 $this->cond_reglement_label = $obj->cond_reglement_label;
1001 $this->cond_reglement_doc = $obj->cond_reglement_doc;
1002 $this->fk_account = $obj->fk_account;
1003 $this->mode_reglement_id = $obj->fk_mode_reglement;
1004 $this->mode_reglement_code = $obj->mode_reglement_code;
1005 $this->mode_reglement = $obj->mode_reglement_label;
1006 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
1007 $this->note = $obj->note_private; // deprecated
1008 $this->note_private = $obj->note_private;
1009 $this->note_public = $obj->note_public;
1010 $this->model_pdf = $obj->model_pdf;
1011 $this->last_main_doc = $obj->last_main_doc;
1012 $this->import_key = $obj->import_key;
1013
1014 //Incoterms
1015 $this->fk_incoterms = $obj->fk_incoterms;
1016 $this->location_incoterms = $obj->location_incoterms;
1017 $this->label_incoterms = $obj->label_incoterms;
1018 $this->transport_mode_id = $obj->fk_transport_mode;
1019
1020 // Multicurrency
1021 $this->fk_multicurrency = $obj->fk_multicurrency;
1022 $this->multicurrency_code = $obj->multicurrency_code;
1023 $this->multicurrency_tx = $obj->multicurrency_tx;
1024 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1025 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1026 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1027
1028 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1029
1030 $this->socid = $obj->socid;
1031
1032 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1033
1034 // Retrieve all extrafield
1035 // fetch optionals attributes and labels
1036 $this->fetch_optionals();
1037
1038 $result = $this->fetch_lines();
1039 if ($result < 0) {
1040 $this->error = $this->db->lasterror();
1041 return -3;
1042 }
1043 } else {
1044 $this->error = 'Bill with id '.$id.' not found';
1045 dol_syslog(get_class($this).'::fetch '.$this->error);
1046 return 0;
1047 }
1048
1049 $this->db->free($resql);
1050 return 1;
1051 } else {
1052 $this->error = "Error ".$this->db->lasterror();
1053 return -1;
1054 }
1055 }
1056
1057
1058 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1064 public function fetch_lines()
1065 {
1066 // phpcs:enable
1067 $this->lines = array();
1068
1069 $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';
1070 $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';
1071 $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';
1072 $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';
1073 $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';
1074 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1075 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1076 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1077 $sql .= ' ORDER BY f.rang, f.rowid';
1078
1079 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1080
1081 $resql_rows = $this->db->query($sql);
1082 if ($resql_rows) {
1083 $num_rows = $this->db->num_rows($resql_rows);
1084 if ($num_rows) {
1085 $i = 0;
1086 while ($i < $num_rows) {
1087 $obj = $this->db->fetch_object($resql_rows);
1088
1089 $line = new SupplierInvoiceLine($this->db);
1090
1091 $line->id = $obj->rowid;
1092 $line->rowid = $obj->rowid;
1093
1094 $line->description = $obj->line_desc;
1095 $line->desc = $obj->line_desc;
1096 $line->date_start = $this->db->jdate($obj->date_start);
1097 $line->date_end = $this->db->jdate($obj->date_end);
1098
1099 $line->product_ref = $obj->product_ref;
1100 $line->ref = $obj->product_ref;
1101 $line->ref_supplier = $obj->ref_supplier;
1102 $line->libelle = $obj->label;
1103 $line->label = $obj->label;
1104 $line->product_barcode = $obj->product_barcode;
1105 $line->product_desc = $obj->product_desc;
1106 $line->subprice = $obj->pu_ht;
1107 $line->pu_ht = $obj->pu_ht; // deprecated
1108 $line->subprice_ttc = $obj->pu_ttc;
1109 $line->pu_ttc = $obj->pu_ttc; // deprcated
1110 $line->vat_src_code = $obj->vat_src_code;
1111 $line->tva_tx = $obj->tva_tx;
1112 $line->localtax1_tx = $obj->localtax1_tx;
1113 $line->localtax2_tx = $obj->localtax2_tx;
1114 $line->localtax1_type = $obj->localtax1_type;
1115 $line->localtax2_type = $obj->localtax2_type;
1116 $line->qty = $obj->qty;
1117 $line->remise_percent = $obj->remise_percent;
1118 $line->fk_remise_except = $obj->fk_remise_except;
1119 //$line->tva = $obj->total_tva; // deprecated
1120 $line->total_ht = $obj->total_ht;
1121 $line->total_ttc = $obj->total_ttc;
1122 $line->total_tva = $obj->total_tva;
1123 $line->total_localtax1 = $obj->total_localtax1;
1124 $line->total_localtax2 = $obj->total_localtax2;
1125 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1126 $line->fk_product = $obj->fk_product;
1127 $line->product_type = $obj->product_type;
1128 $line->product_label = $obj->label;
1129 $line->info_bits = $obj->info_bits;
1130 $line->fk_parent_line = $obj->fk_parent_line;
1131 $line->special_code = $obj->special_code;
1132 $line->rang = $obj->rang;
1133 $line->fk_unit = $obj->fk_unit;
1134
1135 $line->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1136
1137 // Accountancy
1138 $line->fk_code_ventilation = $obj->fk_code_ventilation;
1139
1140 // Multicurrency
1141 $line->fk_multicurrency = $obj->fk_multicurrency;
1142 $line->multicurrency_code = $obj->multicurrency_code;
1143 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1144 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1145 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1146 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1147
1148 // Extra fields
1149 $line->fetch_optionals();
1150
1151 $this->lines[$i] = $line;
1152
1153 $i++;
1154 }
1155 }
1156 $this->db->free($resql_rows);
1157 return 1;
1158 } else {
1159 $this->error = $this->db->error();
1160 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1161 return -3;
1162 }
1163 }
1164
1165
1173 public function update($user = null, $notrigger = 0)
1174 {
1175 global $langs;
1176 $error = 0;
1177
1178 // Clean parameters
1179 if (empty($this->type)) {
1180 $this->type = self::TYPE_STANDARD;
1181 }
1182 if (isset($this->ref)) {
1183 $this->ref = trim($this->ref);
1184 }
1185 if (isset($this->ref_supplier)) {
1186 $this->ref_supplier = trim($this->ref_supplier);
1187 }
1188 if (isset($this->ref_ext)) {
1189 $this->ref_ext = trim($this->ref_ext);
1190 }
1191 if (isset($this->entity)) {
1192 $this->entity = (int) $this->entity;
1193 }
1194 if (isset($this->type)) {
1195 $this->type = (int) $this->type;
1196 }
1197 if (isset($this->subtype)) {
1198 $this->subtype = (int) $this->subtype;
1199 }
1200 if (isset($this->socid)) {
1201 $this->socid = (int) $this->socid;
1202 }
1203 if (isset($this->label)) {
1204 $this->label = trim($this->label);
1205 }
1206 if (isset($this->paid)) {
1207 $this->paid = (int) (bool) $this->paid;
1208 $this->paye = $this->paid; // deprecated
1209 } elseif (isset($this->paye)) {
1210 $this->paid = (int) (bool) $this->paye;
1211 $this->paye = $this->paid; // deprecared
1212 }
1213 if (isset($this->close_code)) {
1214 $this->close_code = trim($this->close_code);
1215 }
1216 if (isset($this->close_note)) {
1217 $this->close_note = trim($this->close_note);
1218 }
1219 if (empty($this->total_ht)) {
1220 $this->total_ht = 0;
1221 }
1222 if (empty($this->total_tva)) {
1223 $this->total_tva = 0;
1224 }
1225 if (empty($this->total_localtax1)) {
1226 $this->total_localtax1 = 0;
1227 }
1228 if (empty($this->total_localtax2)) {
1229 $this->total_localtax2 = 0;
1230 }
1231 if (isset($this->total_ttc)) {
1232 $this->total_ttc = (float) $this->total_ttc;
1233 }
1234 if (isset($this->status)) {
1235 $this->status = (int) $this->status;
1236 $this->statut = $this->status;
1237 } elseif (isset($this->statut)) {
1238 $this->status = (int) $this->statut;
1239 $this->statut = $this->status;
1240 }
1241 if (isset($this->author)) { // TODO: user_creation_id?
1242 $this->author = (int) $this->author;
1243 }
1244 if (isset($this->fk_user_valid)) {
1245 $this->fk_user_valid = (int) $this->fk_user_valid;
1246 }
1247 if (isset($this->fk_facture_source)) {
1248 $this->fk_facture_source = (int) $this->fk_facture_source;
1249 }
1250 if (isset($this->fk_project)) {
1251 if (empty($this->fk_project)) {
1252 $this->fk_project = 0;
1253 } else {
1254 $this->fk_project = (int) $this->fk_project;
1255 }
1256 }
1257 if (isset($this->mode_reglement_id)) {
1258 $this->mode_reglement_id = (int) $this->mode_reglement_id;
1259 }
1260 if (isset($this->cond_reglement_id)) {
1261 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1262 }
1263 if (isset($this->note_private)) {
1264 $this->note_private = trim($this->note_private);
1265 $this->note = $this->note_private;
1266 }
1267 if (isset($this->note_public)) {
1268 $this->note_public = trim($this->note_public);
1269 }
1270 if (isset($this->model_pdf)) {
1271 $this->model_pdf = trim($this->model_pdf);
1272 }
1273 if (isset($this->import_key)) {
1274 $this->import_key = trim($this->import_key);
1275 }
1276
1277
1278 // Check parameters
1279 // Put here code to add control on parameters values
1280
1281 // Update request
1282 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1283 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1284 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1285 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1286 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1287 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1288 $sql .= " subtype=".(isset($this->subtype) ? (int) $this->subtype : "null").",";
1289 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1290 $sql .= " datec=".(dol_strlen((string) $this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1291 $sql .= " datef=".(dol_strlen((string) $this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1292 $sql .= " date_pointoftax=".(dol_strlen((string) $this->date_pointoftax) != 0 ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null').",";
1293 if (dol_strlen((string) $this->date_modification) != 0) {
1294 $sql .= " tms=".(dol_strlen((string) $this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').",";
1295 } elseif (dol_strlen((string) $this->tms) != 0) { // For backward compatibility
1296 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1297 }
1298 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1299 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1300 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1301 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1302 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1303 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1304 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1305 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1306 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1307 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1308 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1309 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1310 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1311 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->vat_reverse_charge) : 0).",";
1312 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1313 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
1314 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1315 $sql .= " date_lim_reglement=".(dol_strlen((string) $this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1316 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1317 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1318 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1319 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1320 $sql .= " WHERE rowid=".((int) $this->id);
1321
1322 $this->db->begin();
1323
1324 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1325 $resql = $this->db->query($sql);
1326
1327 if (!$resql) {
1328 $error++;
1329
1330 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1331 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1332 } else {
1333 $this->errors[] = "Error ".$this->db->lasterror();
1334 }
1335 }
1336
1337 if (!$error) {
1338 $result = $this->insertExtraFields();
1339 if ($result < 0) {
1340 $error++;
1341 }
1342 }
1343
1344 if (!$error && !$notrigger) {
1345 // Call trigger
1346 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1347 if ($result < 0) {
1348 $error++;
1349 }
1350 // End call triggers
1351 }
1352
1353 // Commit or rollback
1354 if ($error) {
1355 foreach ($this->errors as $errmsg) {
1356 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1357 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1358 }
1359 $this->db->rollback();
1360 return -1 * $error;
1361 } else {
1362 $this->db->commit();
1363 return 1;
1364 }
1365 }
1366
1367 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1374 public function insert_discount($idremise)
1375 {
1376 // phpcs:enable
1377 global $conf, $langs;
1378
1379 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1380 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1381
1382 $this->db->begin();
1383
1384 $remise = new DiscountAbsolute($this->db);
1385 $result = $remise->fetch($idremise);
1386
1387 if ($result > 0) {
1388 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1389 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1390 $this->db->rollback();
1391 return -5;
1392 }
1393
1394 $facligne = new SupplierInvoiceLine($this->db);
1395 $facligne->fk_facture_fourn = $this->id;
1396 $facligne->fk_remise_except = $remise->id;
1397 $facligne->desc = $remise->description; // Line description
1398 $facligne->vat_src_code = $remise->vat_src_code;
1399 $facligne->tva_tx = $remise->tva_tx;
1400 $facligne->localtax1_tx = $remise->localtax1_tx;
1401 $facligne->localtax1_type = $remise->localtax1_type;
1402 $facligne->localtax2_tx = $remise->localtax1_tx;
1403 $facligne->localtax2_type = $remise->localtax1_type;
1404 $facligne->subprice = -(float) $remise->amount_ht;
1405 $facligne->fk_product = 0; // Predefined Product ID
1406 $facligne->product_type = 0;
1407 $facligne->qty = 1;
1408 $facligne->remise_percent = 0;
1409 $facligne->rang = -1;
1410 $facligne->info_bits = 2;
1411
1412 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1413 $facligne->rang = 1;
1414 $linecount = count($this->lines);
1415 for ($ii = 1; $ii <= $linecount; $ii++) {
1416 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1417 }
1418 }
1419
1420 // Get buy/cost price of invoice that is source of discount
1421 if ($remise->fk_invoice_supplier_source > 0) {
1422 $srcinvoice = new FactureFournisseur($this->db);
1423 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1424 $totalcostpriceofinvoice = 0;
1425 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1426 $formmargin = new FormMargin($this->db);
1427 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1428 $facligne->pa_ht = $arraytmp['pa_total'];
1429 }
1430
1431 $facligne->total_ht = -(float) $remise->amount_ht;
1432 $facligne->total_tva = -(float) $remise->amount_tva;
1433 $facligne->total_ttc = -(float) $remise->amount_ttc;
1434 $facligne->total_localtax1 = -(float) $remise->total_localtax1;
1435 $facligne->total_localtax2 = -(float) $remise->total_localtax2;
1436
1437 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1438 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1439 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1440 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1441
1442 $lineid = $facligne->insert();
1443 if ($lineid > 0) {
1444 $result = $this->update_price(1);
1445 if ($result > 0) {
1446 // Create link between discount and invoice line
1447 $result = $remise->link_to_invoice($lineid, 0);
1448 if ($result < 0) {
1449 $this->error = $remise->error;
1450 $this->db->rollback();
1451 return -4;
1452 }
1453
1454 $this->db->commit();
1455 return 1;
1456 } else {
1457 $this->error = $facligne->error;
1458 $this->db->rollback();
1459 return -1;
1460 }
1461 } else {
1462 $this->error = $facligne->error;
1463 $this->db->rollback();
1464 return -2;
1465 }
1466 } else {
1467 $this->db->rollback();
1468 return -3;
1469 }
1470 }
1471
1472
1480 public function delete(User $user, $notrigger = 0)
1481 {
1482 global $conf;
1483
1484 $rowid = $this->id;
1485
1486 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1487
1488 // Test to avoid invoice deletion (invoice transferred into accountancy, with payment, ...), same test as Facture::delete()
1489 $result = $this->is_erasable();
1490 if ($result <= 0) {
1491 dol_syslog(get_class($this)."::delete refused, invoice is not erasable (code ".$result.")", LOG_WARNING);
1492 return 0;
1493 }
1494
1495 $error = 0;
1496 $this->db->begin();
1497
1498 if (!$notrigger) {
1499 // Call trigger
1500 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1501 if ($result < 0) {
1502 $this->db->rollback();
1503 return -1;
1504 }
1505 // Fin appel triggers
1506 }
1507
1508 // Remove linked categories.
1509 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_invoice";
1510 $sql .= " WHERE fk_supplier_invoice = ".((int) $this->id);
1511
1512 $result = $this->db->query($sql);
1513 if (!$result) {
1514 $error++;
1515 $this->error = $this->db->lasterror();
1516 $this->errors[] = $this->error;
1517 }
1518
1519 if (!$error) {
1520 // If invoice was converted into a discount not yet consumed, we remove discount
1521 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1522 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1523 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1524 $resql = $this->db->query($sql);
1525
1526 // If invoice has consumned discounts
1527 $this->fetch_lines();
1528 $list_rowid_det = array();
1529 foreach ($this->lines as $key => $invoiceline) {
1530 $list_rowid_det[] = $invoiceline->id;
1531 }
1532
1533 // Consumned discounts are freed
1534 if (count($list_rowid_det)) {
1535 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1536 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1537 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1538
1539 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1540 if (!$this->db->query($sql)) {
1541 $error++;
1542 }
1543 }
1544 }
1545
1546 if (!$error) {
1547 $sql_main_table = MAIN_DB_PREFIX.'facture_fourn_det';
1548 $sql_ef_table = $sql_main_table."_extrafields";
1549 $sqlef = "DELETE FROM $sql_ef_table WHERE fk_object IN (SELECT rowid FROM ".$sql_main_table." WHERE fk_facture_fourn = ".((int) $rowid).")";
1550 $resqlef = $this->db->query($sqlef);
1551 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1552 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1553 $resql = $this->db->query($sql);
1554 if ($resqlef && $resql) {
1555 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1556 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1557 $resql2 = $this->db->query($sql);
1558 if (!$resql2) {
1559 $error++;
1560 }
1561 } else {
1562 $error++;
1563 }
1564 }
1565
1566 if (!$error) {
1567 // Delete linked object
1568 $res = $this->deleteObjectLinked();
1569 if ($res < 0) {
1570 $error++;
1571 }
1572 }
1573
1574 if (!$error) {
1575 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1576 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1577 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1578
1579 // We remove directory
1580 $ref = dol_sanitizeFileName($this->ref);
1581 if ($conf->fournisseur->facture->dir_output && !empty($ref)) {
1582 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1583
1584 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1585 $file = $dir."/".$ref.".pdf";
1586 if (file_exists($file)) {
1587 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1588 $this->error = 'ErrorFailToDeleteFile';
1589 $error++;
1590 }
1591 }
1592 if (file_exists($dir)) {
1593 $res = @dol_delete_dir_recursive($dir);
1594
1595 if (!$res) {
1596 $this->error = 'ErrorFailToDeleteDir';
1597 $error++;
1598 }
1599 }
1600 }
1601 }
1602
1603 // Remove extrafields
1604 if (!$error) {
1605 $result = $this->deleteExtraFields();
1606 if ($result < 0) {
1607 $error++;
1608 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1609 }
1610 }
1611
1612 if (!$error) {
1613 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1614 $this->db->commit();
1615 return 1;
1616 } else {
1617 $this->error = $this->db->lasterror();
1618 $this->db->rollback();
1619 return -$error;
1620 }
1621 }
1622
1623
1624 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1635 public function set_paid($user, $close_code = '', $close_note = '')
1636 {
1637 // phpcs:enable
1638 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1639 return $this->setPaid($user, $close_code, $close_note);
1640 }
1641
1650 public function setPaid($user, $close_code = '', $close_note = '')
1651 {
1652 $error = 0;
1653
1654 if ($this->paid != 1) {
1655 $this->db->begin();
1656
1657 $now = dol_now();
1658
1659 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1660
1661 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1662 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1663 if (!$close_code) {
1664 $sql .= ', paye=1';
1665 }
1666 if ($close_code) {
1667 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1668 }
1669 if ($close_note) {
1670 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1671 }
1672 $sql .= ', fk_user_closing = '.((int) $user->id);
1673 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1674 $sql .= ' WHERE rowid = '.((int) $this->id);
1675
1676 $resql = $this->db->query($sql);
1677 if ($resql) {
1678 // Call trigger
1679 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1680 if ($result < 0) {
1681 $error++;
1682 }
1683 // End call triggers
1684 } else {
1685 $error++;
1686 $this->error = $this->db->error();
1687 dol_print_error($this->db);
1688 }
1689
1690 if (!$error) {
1691 $this->db->commit();
1692 return 1;
1693 } else {
1694 $this->db->rollback();
1695 return -1;
1696 }
1697 } else {
1698 return 0;
1699 }
1700 }
1701
1702 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1713 public function set_unpaid($user)
1714 {
1715 // phpcs:enable
1716 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1717 return $this->setUnpaid($user);
1718 }
1719
1728 public function setUnpaid($user)
1729 {
1730 $error = 0;
1731
1732 $this->db->begin();
1733
1734 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1735 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1736 $sql .= ' date_closing=null,';
1737 $sql .= ' fk_user_closing=null';
1738 $sql .= ' WHERE rowid = '.((int) $this->id);
1739
1740 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1741 $resql = $this->db->query($sql);
1742 if ($resql) {
1743 // Call trigger
1744 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1745 if ($result < 0) {
1746 $error++;
1747 }
1748 // End call triggers
1749 } else {
1750 $error++;
1751 $this->error = $this->db->error();
1752 dol_print_error($this->db);
1753 }
1754
1755 if (!$error) {
1756 $this->db->commit();
1757 return 1;
1758 } else {
1759 $this->db->rollback();
1760 return -1;
1761 }
1762 }
1763
1774 public function setCanceled($user, $close_code = '', $close_note = '')
1775 {
1776 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1777
1778 $this->db->begin();
1779
1780 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1781 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1782 if ($close_code) {
1783 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1784 }
1785 if ($close_note) {
1786 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1787 }
1788 $sql .= " WHERE rowid = ".((int) $this->id);
1789
1790 $resql = $this->db->query($sql);
1791 if ($resql) {
1792 // Bound discounts are deducted from the invoice
1793 // as they have not been used since the invoice is abandoned.
1794 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1795 $sql .= ' SET fk_invoice_supplier = NULL';
1796 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1797
1798 $resql = $this->db->query($sql);
1799 if ($resql) {
1800 // Call trigger
1801 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1802 if ($result < 0) {
1803 $this->db->rollback();
1804 return -1;
1805 }
1806 // End call triggers
1807
1808 $this->db->commit();
1809 return 1;
1810 } else {
1811 $this->error = $this->db->error()." sql=".$sql;
1812 $this->db->rollback();
1813 return -1;
1814 }
1815 } else {
1816 $this->error = $this->db->error()." sql=".$sql;
1817 $this->db->rollback();
1818 return -2;
1819 }
1820 }
1821
1831 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1832 {
1833 global $mysoc, $conf, $langs;
1834
1835 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1836
1837 $now = dol_now();
1838
1839 $error = 0;
1840 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1841
1842 // Force to have object complete for checks
1843 $this->fetch_thirdparty();
1844 $this->fetch_lines();
1845
1846 // Check parameters
1847 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1848 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1849 return 0;
1850 }
1851 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ', '/').'/', $this->ref_supplier)) {
1852 $langs->load("errors");
1853 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1854 return -1;
1855 }
1856 if (count($this->lines) <= 0) {
1857 $langs->load("errors");
1858 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1859 return -1;
1860 }
1861
1862 // Check for mandatory fields in thirdparty (defined into setup)
1863 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1864 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1865 foreach ($array_to_check as $key) {
1866 $keymin = strtolower($key);
1867 if ($keymin == 'accountancy_code_supplier') {
1868 $keymin = 'code_compta_fournisseur';
1869 }
1870 if (!property_exists($this->thirdparty, $keymin)) {
1871 continue;
1872 }
1873 $vallabel = $this->thirdparty->$keymin;
1874
1875 $i = (int) preg_replace('/[^0-9]/', '', $key);
1876 if ($i > 0) {
1877 if ($this->thirdparty->isACompany()) {
1878 // Check for mandatory prof id (but only if country is other than ours)
1879 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1880 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1881 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1882 $langs->load("errors");
1883 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1884 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1885 return -1;
1886 }
1887 }
1888 }
1889 } else {
1890 if ($key == 'EMAIL') {
1891 // Check for mandatory
1892 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1893 $langs->load("errors");
1894 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1895 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1896 return -1;
1897 }
1898 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1899 // Check for mandatory
1900 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1901 $langs->load("errors");
1902 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1903 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1904 return -1;
1905 }
1906 }
1907 }
1908 }
1909 }
1910
1911 $this->db->begin();
1912
1913 // Define new ref
1914 if ($force_number) {
1915 $num = $force_number;
1916 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1917 $num = $this->getNextNumRef($this->thirdparty);
1918 } else {
1919 $num = (string) $this->ref;
1920 }
1921 $this->newref = dol_sanitizeFileName($num);
1922
1923 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1924 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1925 $sql .= " WHERE rowid = ".((int) $this->id);
1926
1927 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1928 $resql = $this->db->query($sql);
1929 if ($resql) {
1930 // If we increment the main product and its components upon validation of the supplier invoice
1931 if (isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1932 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1933 $langs->load("agenda");
1934
1935 $cpt = count($this->lines);
1936 for ($i = 0; $i < $cpt; $i++) {
1937 if ($this->lines[$i]->fk_product > 0) {
1938 $mouvP = new MouvementStock($this->db);
1939 $mouvP->origin = &$this;
1940 $mouvP->setOrigin($this->element, $this->id);
1941 // We increase stock for product
1942 $up_ht_disc = $this->lines[$i]->subprice;
1943 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1944 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1945 }
1947 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1948 } else {
1949 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1950 }
1951 if ($result < 0) {
1952 $this->setErrorsFromObject($mouvP);
1953 return -2;
1954 }
1955 }
1956 }
1957 }
1958
1959 // Triggers call
1960 if (empty($notrigger)) {
1961 // Call trigger
1962 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1963 if ($result < 0) {
1964 $error++;
1965 }
1966 // End call triggers
1967 }
1968
1969 if (!$error) {
1970 $this->oldref = $this->ref;
1971
1972 // Rename directory if dir was a temporary ref
1973 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1974 // Now we rename also files into index
1975 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->newref)."'";
1976 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1977 $resql = $this->db->query($sql);
1978 if (!$resql) {
1979 $error++;
1980 $this->error = $this->db->lasterror();
1981 }
1982 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->newref)."'";
1983 $sql .= " WHERE filepath = 'fournisseur/facture/".$this->db->escape(get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier')).$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1984 $resql = $this->db->query($sql);
1985 if (!$resql) {
1986 $error++;
1987 $this->error = $this->db->lasterror();
1988 }
1989
1990 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1991 $oldref = dol_sanitizeFileName($this->ref);
1992 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1993 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1994 if (!$error && file_exists($dirsource)) {
1995 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1996
1997 if (@rename($dirsource, $dirdest)) {
1998 dol_syslog("Rename ok");
1999 // Rename docs starting with $oldref with $this->newref
2000 $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, '/'));
2001 foreach ($listoffiles as $fileentry) {
2002 $dirsource = $fileentry['name'];
2003 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
2004 $dirsource = $fileentry['path'].'/'.$dirsource;
2005 $dirdest = $fileentry['path'].'/'.$dirdest;
2006 @rename($dirsource, $dirdest);
2007 }
2008 }
2009 }
2010 }
2011 }
2012
2013 // Set new ref and define current status
2014 if (!$error) {
2015 $this->ref = $this->newref;
2016 $this->statut = self::STATUS_VALIDATED;
2018 //$this->date_validation=$now; this is stored into log table
2019 }
2020
2021 if (!$error) {
2022 $this->db->commit();
2023 return 1;
2024 } else {
2025 $this->db->rollback();
2026 return -1;
2027 }
2028 } else {
2029 $this->error = $this->db->error();
2030 $this->db->rollback();
2031 return -1;
2032 }
2033 }
2034
2043 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
2044 {
2045 // phpcs:enable
2046 global $conf, $langs;
2047
2048 $error = 0;
2049
2050 if ($this->status == self::STATUS_DRAFT) {
2051 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
2052 return 0;
2053 }
2054
2055 dol_syslog(__METHOD__, LOG_DEBUG);
2056
2057 $this->db->begin();
2058
2059 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2060 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2061 $sql .= " WHERE rowid = ".((int) $this->id);
2062
2063 $result = $this->db->query($sql);
2064 if ($result) {
2065 $this->oldcopy = clone $this;
2066
2067 // If we increment the main product and its components upon supplier invoice validation, we decrement upon customer invoice validation
2068 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2069 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2070 $langs->load("agenda");
2071
2072 $cpt = count($this->lines);
2073 for ($i = 0; $i < $cpt; $i++) {
2074 if ($this->lines[$i]->fk_product > 0) {
2075 $mouvP = new MouvementStock($this->db);
2076 $mouvP->origin = &$this;
2077 $mouvP->setOrigin($this->element, $this->id);
2078 // We increase stock for product
2080 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2081 } else {
2082 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2083 }
2084 }
2085 }
2086 }
2087 // Triggers call
2088 if (empty($notrigger)) {
2089 // Call trigger
2090 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2091 if ($result < 0) {
2092 $error++;
2093 }
2094 // End call triggers
2095 }
2096 if ($error == 0) {
2097 $this->db->commit();
2098 return 1;
2099 } else {
2100 $this->db->rollback();
2101 return -1;
2102 }
2103 } else {
2104 $this->error = $this->db->error();
2105 $this->db->rollback();
2106 return -1;
2107 }
2108 }
2109
2110
2145 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 = '')
2146 {
2147 global $langs, $mysoc;
2148
2149 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);
2150 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2151
2152 if ($this->status == self::STATUS_DRAFT) {
2153 // Clean parameters
2154 if (empty($remise_percent)) {
2155 $remise_percent = 0;
2156 }
2157 if (empty($qty)) {
2158 $qty = 0;
2159 }
2160 if (empty($info_bits)) {
2161 $info_bits = 0;
2162 }
2163 if (empty($rang)) {
2164 $rang = 0;
2165 }
2166 if (empty($fk_code_ventilation)) {
2167 $fk_code_ventilation = 0;
2168 }
2169 if (empty($txtva)) {
2170 $txtva = 0;
2171 }
2172 if (empty($txlocaltax1)) {
2173 $txlocaltax1 = 0;
2174 }
2175 if (empty($txlocaltax2)) {
2176 $txlocaltax2 = 0;
2177 }
2178
2179 $remise_percent = price2num($remise_percent);
2180 $qty = price2num($qty);
2181 $pu = price2num($pu);
2182 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2183 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2184 }
2185 $txlocaltax1 = price2num($txlocaltax1);
2186 $txlocaltax2 = price2num($txlocaltax2);
2187
2188 if ($date_start && $date_end && $date_start > $date_end) {
2189 $langs->load("errors");
2190 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2191 return -1;
2192 }
2193
2194 $this->db->begin();
2195
2196 if ($fk_product > 0) {
2197 if (getDolGlobalInt('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
2198 // Check quantity is enough
2199 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2200 $prod = new ProductFournisseur($this->db);
2201 if ($prod->fetch($fk_product) > 0) {
2202 $product_type = $prod->type;
2203 $label = $prod->label;
2204 $fk_prod_fourn_price = 0;
2205
2206 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2207 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2208 $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
2209 if ($result > 0) {
2210 if (empty($pu)) {
2211 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2212 }
2213 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2214 // is remise percent not keyed but present for the product we add it
2215 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2216 $remise_percent = $prod->remise_percent;
2217 }
2218 }
2219 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2220 $langs->load("errors");
2221 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2222 $this->db->rollback();
2223 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2224 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2225 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2226 return -1;
2227 }
2228 if ($result == -1) {
2229 $langs->load("errors");
2230 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2231 $this->db->rollback();
2232 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2233 return -1;
2234 }
2235 if ($result < -1) {
2236 $this->error = $prod->error;
2237 $this->db->rollback();
2238 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2239 return -1;
2240 }
2241 } else {
2242 $this->error = $prod->error;
2243 $this->db->rollback();
2244 return -1;
2245 }
2246 }
2247 } else {
2248 $product_type = $type;
2249 }
2250
2251 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2252 $pu = 0;
2253 }
2254
2255 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2256
2257 // Clean vat code
2258 $reg = array();
2259 $vat_src_code = '';
2260 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2261 $vat_src_code = $reg[1];
2262 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2263 }
2264
2265 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2266 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2267 // and this is done at the line level, which has its own VAT rate
2268
2269 $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);
2270 $total_ht = $tabprice[0];
2271 $total_tva = $tabprice[1];
2272 $total_ttc = $tabprice[2];
2273 $total_localtax1 = $tabprice[9];
2274 $total_localtax2 = $tabprice[10];
2275 $pu_ht = $tabprice[3];
2276
2277 // MultiCurrency
2278 $multicurrency_total_ht = $tabprice[16];
2279 $multicurrency_total_tva = $tabprice[17];
2280 $multicurrency_total_ttc = $tabprice[18];
2281 $pu_ht_devise = $tabprice[19];
2282
2283 // Check parameters
2284 if ($type < 0) {
2285 return -1;
2286 }
2287
2288 if ($rang < 0) {
2289 $rangmax = $this->line_max();
2290 $rang = $rangmax + 1;
2291 }
2292
2293 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2294 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2295 $apply_abs_price_on_credit_note = false;
2296 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2297 $apply_abs_price_on_credit_note = true;
2298 }
2299
2300 // Insert line
2301 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2302
2303 $supplierinvoiceline->context = $this->context;
2304
2305 $supplierinvoiceline->fk_facture_fourn = $this->id;
2306 //$supplierinvoiceline->label=$label; // deprecated
2307 $supplierinvoiceline->desc = $desc;
2308 $supplierinvoiceline->ref_supplier = $ref_supplier;
2309
2310 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2311 $supplierinvoiceline->subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2312
2313 $supplierinvoiceline->vat_src_code = $vat_src_code;
2314 $supplierinvoiceline->tva_tx = $txtva;
2315 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2316 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2317 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2318 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2319
2320 $supplierinvoiceline->total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht); // For credit note and if qty is negative, total is negative
2321 $supplierinvoiceline->total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva); // For credit note and if qty is negative, total is negative
2322 $supplierinvoiceline->total_localtax1 = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_localtax1) : (float) $total_localtax1); // For credit note and if qty is negative, total is negative
2323 $supplierinvoiceline->total_localtax2 = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_localtax2) : (float) $total_localtax2); // For credit note and if qty is negative, total is negative
2324 $supplierinvoiceline->total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc); // For credit note and if qty is negative, total is negative
2325
2326 $supplierinvoiceline->fk_product = $fk_product;
2327 $supplierinvoiceline->product_type = $type;
2328 $supplierinvoiceline->remise_percent = $remise_percent;
2329 $supplierinvoiceline->date_start = $date_start;
2330 $supplierinvoiceline->date_end = $date_end;
2331 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2332 $supplierinvoiceline->rang = $rang;
2333 $supplierinvoiceline->info_bits = $info_bits;
2334 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2335
2336
2337 $supplierinvoiceline->special_code = (int) $special_code;
2338 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2339 $supplierinvoiceline->origin = $this->origin;
2340 $supplierinvoiceline->origin_type = $origin_type;
2341 $supplierinvoiceline->origin_id = $origin_id;
2342 $supplierinvoiceline->fk_unit = $fk_unit;
2343
2344 // Multicurrency
2345 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2346 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2347 $supplierinvoiceline->multicurrency_subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht_devise) : (float) $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2348
2349 $supplierinvoiceline->multicurrency_total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_ht) : (float) $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2350 $supplierinvoiceline->multicurrency_total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_tva) : (float) $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2351 $supplierinvoiceline->multicurrency_total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $multicurrency_total_ttc) : (float) $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2352
2353 if (is_array($array_options) && count($array_options) > 0) {
2354 $supplierinvoiceline->array_options = $array_options;
2355 }
2356
2357 $result = $supplierinvoiceline->insert($notrigger);
2358 if ($result > 0) {
2359 // Update denormalized fields at the order level
2360 $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.
2361
2362 if ($result > 0) {
2363 if (!isset($this->context['createfromclone'])) {
2364 if (!empty($fk_parent_line)) {
2365 // Always reorder if child line
2366 $this->line_order(true, 'DESC');
2367 } elseif ($rang > 0 && $rang <= count($this->lines)) {
2368 // Update all rank of all other lines starting from the same $ranktouse
2369 $linecount = count($this->lines);
2370 for ($ii = $rang; $ii <= $linecount; $ii++) {
2371 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2372 }
2373 }
2374
2375 $this->lines[] = $supplierinvoiceline;
2376 }
2377
2378 $this->db->commit();
2379 return $supplierinvoiceline->id;
2380 } else {
2381 $this->error = $this->db->error();
2382 $this->db->rollback();
2383 return -1;
2384 }
2385 } else {
2386 $this->error = $supplierinvoiceline->error;
2387 $this->errors = $supplierinvoiceline->errors;
2388 $this->db->rollback();
2389 return -2;
2390 }
2391 } else {
2392 return 0;
2393 }
2394 }
2395
2421 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)
2422 {
2423 global $mysoc, $langs;
2424
2425 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);
2426 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2427
2428 $pu = price2num($pu);
2429 $qty = price2num($qty);
2430 $remise_percent = (float) price2num($remise_percent);
2431 $pu_devise = price2num($pu_devise);
2432
2433 // Check parameters
2434 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2435 if ($type < 0) {
2436 return -1;
2437 }
2438
2439 if ($date_start && $date_end && $date_start > $date_end) {
2440 $langs->load("errors");
2441 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2442 return -1;
2443 }
2444
2445 // Clean parameters
2446 if (empty($vatrate)) {
2447 $vatrate = 0;
2448 }
2449 if (empty($txlocaltax1)) {
2450 $txlocaltax1 = 0;
2451 }
2452 if (empty($txlocaltax2)) {
2453 $txlocaltax2 = 0;
2454 }
2455
2456 $txlocaltax1 = (float) price2num($txlocaltax1);
2457 $txlocaltax2 = (float) price2num($txlocaltax2);
2458
2459 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
2460 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
2461 // and this is done at the line level, which has its own VAT rate
2462
2463 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2464
2465 $reg = array();
2466
2467 // Clean vat code
2468 $vat_src_code = '';
2469 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2470 $vat_src_code = $reg[1];
2471 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2472 }
2473
2474 $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);
2475 $total_ht = $tabprice[0];
2476 $total_tva = $tabprice[1];
2477 $total_ttc = $tabprice[2];
2478 $pu_ht = $tabprice[3];
2479 $pu_tva = $tabprice[4];
2480 $pu_ttc = $tabprice[5];
2481 $total_localtax1 = $tabprice[9];
2482 $total_localtax2 = $tabprice[10];
2483
2484 // MultiCurrency
2485 $multicurrency_total_ht = $tabprice[16];
2486 $multicurrency_total_tva = $tabprice[17];
2487 $multicurrency_total_ttc = $tabprice[18];
2488 $pu_ht_devise = $tabprice[19];
2489
2490 if (empty($info_bits)) {
2491 $info_bits = 0;
2492 }
2493
2494 // Fetch current line from the database and then clone the object and set it in $oldline property
2495 $line = new SupplierInvoiceLine($this->db);
2496 $line->fetch($id);
2497 $line->fetch_optionals();
2498
2499 $staticline = clone $line;
2500
2501 if ($idproduct) {
2502 $product = new Product($this->db);
2503 $result = $product->fetch($idproduct);
2504 $product_type = $product->type;
2505 } else {
2506 $idproduct = $staticline->fk_product;
2507 $product_type = $type;
2508 }
2509
2510 $line->oldline = $staticline;
2511 $line->context = $this->context;
2512
2513 // Same gate as on the customer invoice side: the -abs() forcing on credit note lines is relaxed only when
2514 // both FACTURE_ENABLE_NEGATIVE_LINES and INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN are enabled.
2515 $apply_abs_price_on_credit_note = false;
2516 if ($this->type == self::TYPE_CREDIT_NOTE && !(getDolGlobalInt('FACTURE_ENABLE_NEGATIVE_LINES') && getDolGlobalInt('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN'))) {
2517 $apply_abs_price_on_credit_note = true;
2518 }
2519
2520 $line->description = $desc;
2521 $line->desc = $desc;
2522
2523 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : (float) $qty); // For credit note, quantity is always positive and unit price negative
2524
2525 $line->subprice = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ht) : (float) $pu_ht); // For credit note, unit price always negative, always positive otherwise
2526 $line->pu_ht = $line->subprice; // deprecated
2527 $line->subprice_ttc = ($apply_abs_price_on_credit_note ? -abs((float) $pu_ttc) : (float) $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2528 $line->pu_ttc = $line->subprice_ttc; // deprecated
2529
2530 $line->remise_percent = $remise_percent;
2531 $line->ref_supplier = $ref_supplier;
2532
2533 $line->date_start = $date_start;
2534 $line->date_end = $date_end;
2535
2536 $line->vat_src_code = $vat_src_code;
2537 $line->tva_tx = $vatrate;
2538 $line->localtax1_tx = $txlocaltax1;
2539 $line->localtax2_tx = $txlocaltax2;
2540 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2541 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2542
2543 $line->total_ht = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ht) : (float) $total_ht);
2544 $line->total_tva = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_tva) : (float) $total_tva);
2545 $line->total_localtax1 = (float) $total_localtax1;
2546 $line->total_localtax2 = (float) $total_localtax2;
2547 $line->total_ttc = (($apply_abs_price_on_credit_note || $qty < 0) ? -abs((float) $total_ttc) : (float) $total_ttc);
2548
2549 $line->fk_product = $idproduct;
2550 $line->product_type = $product_type;
2551 $line->info_bits = $info_bits;
2552 $line->fk_unit = $fk_unit;
2553 $line->rang = $rang;
2554
2555 if (is_array($array_options) && count($array_options) > 0) {
2556 // We replace values in this->line->array_options only for entries defined into $array_options
2557 foreach ($array_options as $key => $value) {
2558 $line->array_options[$key] = $array_options[$key];
2559 }
2560 }
2561
2562 // Multicurrency
2563 $line->multicurrency_subprice = (float) $pu_ht_devise;
2564 $line->multicurrency_total_ht = (float) $multicurrency_total_ht;
2565 $line->multicurrency_total_tva = (float) $multicurrency_total_tva;
2566 $line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
2567
2568 $res = $line->update($notrigger);
2569
2570 if ($res < 1) {
2571 $this->setErrorsFromObject($line);
2572 } else {
2573 // Update total price into invoice record
2574 $res = $this->update_price(1, 'auto', 0, $this->thirdparty);
2575 }
2576
2577 return $res;
2578 }
2579
2587 public function deleteLine($rowid, $notrigger = 0)
2588 {
2589 if (!$rowid) {
2590 $rowid = $this->id;
2591 }
2592
2593 $this->db->begin();
2594
2595 // Free the discount linked to a line of invoice
2596 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2597 $sql .= ' SET fk_invoice_supplier_line = NULL';
2598 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2599
2600 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2601 $result = $this->db->query($sql);
2602 if (!$result) {
2603 $this->error = $this->db->error();
2604 $this->db->rollback();
2605 return -2;
2606 }
2607
2608 $line = new SupplierInvoiceLine($this->db);
2609
2610 if ($line->fetch($rowid) < 1) {
2611 return -1;
2612 }
2613
2614 $res = $line->delete($notrigger);
2615
2616 if ($res < 1) {
2617 $this->errors[] = $line->error;
2618 $this->db->rollback();
2619 return -3;
2620 } else {
2621 $res = $this->update_price(1);
2622
2623 if ($res > 0) {
2624 $this->db->commit();
2625 return 1;
2626 } else {
2627 $this->db->rollback();
2628 $this->error = $this->db->lasterror();
2629 return -4;
2630 }
2631 }
2632 }
2633
2634
2641 public function info($id)
2642 {
2643 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2644 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2645 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2646 $sql .= ' WHERE c.rowid = '.((int) $id);
2647
2648 $result = $this->db->query($sql);
2649 if ($result) {
2650 if ($this->db->num_rows($result)) {
2651 $obj = $this->db->fetch_object($result);
2652
2653 $this->id = $obj->rowid;
2654
2655 $this->user_creation_id = $obj->fk_user_author;
2656 $this->user_validation_id = $obj->fk_user_valid;
2657 $this->user_modification_id = $obj->fk_user_modif;
2658 $this->date_creation = $this->db->jdate($obj->datec);
2659 $this->date_modification = $this->db->jdate($obj->datem);
2660 //$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)
2661 }
2662 $this->db->free($result);
2663 } else {
2664 dol_print_error($this->db);
2665 }
2666 }
2667
2668 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2677 public function list_replacable_supplier_invoices($socid = 0)
2678 {
2679 // phpcs:enable
2680 global $conf;
2681
2682 $return = array();
2683
2684 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2685 $sql .= " ff.rowid as rowidnext";
2686 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2687 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2688 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2689 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2690 $sql .= " AND f.entity = ".((int) $conf->entity);
2691 $sql .= " AND f.paye = 0"; // Not closed completely
2692 $sql .= " AND pf.fk_paiementfourn IS NULL"; // No payment already done
2693 $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)
2694 if ($socid > 0) {
2695 $sql .= " AND f.fk_soc = ".((int) $socid);
2696 }
2697 $sql .= " ORDER BY f.ref";
2698
2699 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2700 $resql = $this->db->query($sql);
2701 if ($resql) {
2702 while ($obj = $this->db->fetch_object($resql)) {
2703 $return[$obj->rowid] = array(
2704 'id' => $obj->rowid,
2705 'ref' => $obj->ref,
2706 'status' => $obj->fk_statut
2707 );
2708 }
2709 //print_r($return);
2710 return $return;
2711 } else {
2712 $this->error = $this->db->error();
2713 return -1;
2714 }
2715 }
2716
2717 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2726 public function list_qualified_avoir_supplier_invoices($socid = 0)
2727 {
2728 // phpcs:enable
2729 global $conf;
2730
2731 $return = array();
2732
2733 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye as paid, pf.fk_paiementfourn";
2734 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2735 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2736 $sql .= " WHERE f.entity = ".((int) $conf->entity);
2737 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2738 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2739 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2740 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2741 if ($socid > 0) {
2742 $sql .= " AND f.fk_soc = ".((int) $socid);
2743 }
2744 $sql .= " ORDER BY f.ref";
2745
2746 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2747 $resql = $this->db->query($sql);
2748 if ($resql) {
2749 while ($obj = $this->db->fetch_object($resql)) {
2750 $qualified = 0;
2751 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2752 $qualified = 1;
2753 }
2754 if ($obj->fk_statut == self::STATUS_CLOSED) {
2755 $qualified = 1;
2756 }
2757 if ($qualified) {
2758 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2759 $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);
2760 }
2761 }
2762
2763 return $return;
2764 } else {
2765 $this->error = $this->db->error();
2766 return -1;
2767 }
2768 }
2769
2770 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2777 public function load_board($user)
2778 {
2779 // phpcs:enable
2780 global $conf, $langs;
2781
2782 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2783 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2784 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2785 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2786 }
2787 $sql .= ' WHERE ff.paye = 0';
2788 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2789 $sql .= " AND ff.entity = ".((int) $conf->entity);
2790 if ($user->socid) {
2791 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2792 }
2793
2794 $resql = $this->db->query($sql);
2795 if ($resql) {
2796 $langs->load("bills");
2797 $now = dol_now();
2798
2799 $response = new WorkboardResponse();
2800 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2801 $response->label = $langs->trans("SupplierBillsToPay");
2802 $response->labelShort = $langs->trans("StatusToPay");
2803
2804 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2805 $response->img = img_object($langs->trans("Bills"), "bill");
2806
2807 $facturestatic = new FactureFournisseur($this->db);
2808
2809 while ($obj = $this->db->fetch_object($resql)) {
2810 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2811 $facturestatic->statut = $obj->status; // For backward compatibility
2812 $facturestatic->status = $obj->status;
2813
2814 $response->nbtodo++;
2815 $response->total += (float) $obj->total_ht;
2816
2817 if ($facturestatic->hasDelay()) {
2818 $response->nbtodolate++;
2819 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2820 }
2821 }
2822
2823 $this->db->free($resql);
2824 return $response;
2825 } else {
2826 dol_print_error($this->db);
2827 $this->error = $this->db->error();
2828 return -1;
2829 }
2830 }
2831
2839 public function getTooltipContentArray($params)
2840 {
2841 global $conf, $langs, $mysoc;
2842
2843 $langs->load('bills');
2844
2845 $datas = [];
2846 $moretitle = $params['moretitle'] ?? '';
2847
2848 $picto = $this->picto;
2849 if ($this->type == self::TYPE_REPLACEMENT) {
2850 $picto .= 'r'; // Replacement invoice
2851 }
2852 if ($this->type == self::TYPE_CREDIT_NOTE) {
2853 $picto .= 'a'; // Credit note
2854 }
2855 if ($this->type == self::TYPE_DEPOSIT) {
2856 $picto .= 'd'; // Deposit invoice
2857 }
2858
2859 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2860 if ($this->type == self::TYPE_REPLACEMENT) {
2861 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplacement").'</u>';
2862 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2863 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2864 } elseif ($this->type == self::TYPE_DEPOSIT) {
2865 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2866 }
2867 if (isset($this->status)) {
2868 $alreadypaid = -1;
2869 if (isset($this->totalpaid)) {
2870 $alreadypaid = $this->totalpaid;
2871 }
2872
2873 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2874 }
2875 if ($moretitle) {
2876 $datas['picto'] .= ' - '.$moretitle;
2877 }
2878 if (!empty($this->ref)) {
2879 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2880 }
2881 if (!empty($this->ref_supplier)) {
2882 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2883 }
2884 if (!empty($this->label)) {
2885 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2886 }
2887 if (!empty($this->date)) {
2888 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2889 }
2890 if (!empty($this->date_echeance)) {
2891 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2892 }
2893 if (!empty($this->total_ht)) {
2894 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2895 }
2896 if (!empty($this->total_tva)) {
2897 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2898 }
2899 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2900 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2901 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2902 }
2903 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2904 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2905 }
2906 if (!empty($this->revenuestamp)) {
2907 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2908 }
2909 if (!empty($this->total_ttc)) {
2910 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2911 }
2912 return $datas;
2913 }
2914
2928 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2929 {
2930 global $langs, $user, $hookmanager;
2931
2932 $result = '';
2933
2934 if ($option == 'withdraw') {
2935 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2936 } elseif ($option == 'document') {
2937 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2938 } else {
2939 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2940 }
2941
2942 if ($short) {
2943 return $url;
2944 }
2945
2946 if ($option !== 'nolink') {
2947 // Add param to save lastsearch_values or not
2948 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2949 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2950 $add_save_lastsearch_values = 1;
2951 }
2952 if ($add_save_lastsearch_values) {
2953 $url .= '&save_lastsearch_values=1';
2954 }
2955 }
2956
2957 $picto = $this->picto;
2958 if ($this->type == self::TYPE_REPLACEMENT) {
2959 $picto .= 'r'; // Replacement invoice
2960 }
2961 if ($this->type == self::TYPE_CREDIT_NOTE) {
2962 $picto .= 'a'; // Credit note
2963 }
2964 if ($this->type == self::TYPE_DEPOSIT) {
2965 $picto .= 'd'; // Deposit invoice
2966 }
2967
2968 $params = [
2969 'id' => $this->id,
2970 'objecttype' => $this->element,
2971 'option' => $option,
2972 'moretitle' => $moretitle,
2973 ];
2974 $classfortooltip = 'classfortooltip';
2975 $dataparams = '';
2976 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2977 $classfortooltip = 'classforajaxtooltip';
2978 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2979 $label = '';
2980 } else {
2981 $label = implode($this->getTooltipContentArray($params));
2982 }
2983
2984 $ref = $this->ref;
2985 if (empty($ref)) {
2986 $ref = $this->id;
2987 }
2988
2989 $linkclose = '';
2990 if (empty($notooltip)) {
2991 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2992 $label = $langs->trans("ShowSupplierInvoice");
2993 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2994 }
2995 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2996 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2997 }
2998
2999 $linkstart = '<a href="'.$url.'"';
3000 $linkstart .= $linkclose.'>';
3001 $linkend = '</a>';
3002
3003 $result .= $linkstart;
3004 if ($withpicto) {
3005 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
3006 }
3007 if ($withpicto != 2) {
3008 $result .= ($max ? dol_trunc($ref, $max) : $ref);
3009 }
3010 $result .= $linkend;
3011
3012 if ($addlinktonotes) {
3013 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3014 if ($txttoshow) {
3015 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3016 $result .= ' <span class="note inline-block">';
3017 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3018 $result .= img_picto('', 'note');
3019 $result .= '</a>';
3020 $result .= '</span>';
3021 }
3022 }
3023 global $action;
3024 $hookmanager->initHooks(array($this->element . 'dao'));
3025 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3026 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3027 if ($reshook > 0) {
3028 $result = $hookmanager->resPrint;
3029 } else {
3030 $result .= $hookmanager->resPrint;
3031 }
3032 return $result;
3033 }
3034
3045 public function setCategories($categories)
3046 {
3047 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
3048 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_INVOICE);
3049 }
3050
3059 public function getNextNumRef($soc, $mode = 'next')
3060 {
3061 global $db, $langs, $conf;
3062 $langs->load("orders");
3063
3064 // Clean parameters (if not defined or using deprecated value)
3065 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
3066 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
3067 }
3068
3069 $mybool = false;
3070
3071 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
3072 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
3073
3074 // Include file with class
3075 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3076
3077 foreach ($dirmodels as $reldir) {
3078 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
3079
3080 // Load file with numbering class (if found)
3081 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3082 }
3083
3084 if (!$mybool) {
3085 dol_print_error(null, "Failed to include file ".$file);
3086 return '';
3087 }
3088
3089 $obj = new $classname();
3090 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3091 $numref = "";
3092 $numref = $obj->getNextValue($soc, $this, $mode);
3093
3094 if ($numref != "") {
3095 return $numref;
3096 } else {
3097 $this->error = $obj->error;
3098 return -1;
3099 }
3100 }
3101
3102
3111 public function initAsSpecimen($option = '')
3112 {
3113 global $langs, $conf;
3114 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3115
3116 $now = dol_now();
3117
3118 // Load array of products prodids
3119 $num_prods = 0;
3120 $prodids = array();
3121
3122 $sql = "SELECT rowid";
3123 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3124 $sql .= " WHERE entity IN (".getEntity('product').")";
3125 $sql .= $this->db->plimit(100);
3126
3127 $resql = $this->db->query($sql);
3128 if ($resql) {
3129 $num_prods = $this->db->num_rows($resql);
3130 $i = 0;
3131 while ($i < $num_prods) {
3132 $i++;
3133 $row = $this->db->fetch_row($resql);
3134 $prodids[$i] = $row[0];
3135 }
3136 }
3137
3138 // Initialise parameters
3139 $this->id = 0;
3140 $this->ref = 'SPECIMEN';
3141 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3142 $this->specimen = 1;
3143 $this->socid = 1;
3144 $this->date = $now;
3145 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3146 $this->cond_reglement_code = 'RECEP';
3147 $this->mode_reglement_code = 'CHQ';
3148
3149 $this->note_public = 'This is a comment (public)';
3150 $this->note_private = 'This is a comment (private)';
3151
3152 $this->multicurrency_tx = 1;
3153 $this->multicurrency_code = $conf->currency;
3154
3155 $xnbp = 0;
3156 if (empty($option) || $option != 'nolines') {
3157 // Lines
3158 $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)
3159 while ($xnbp < $nbp) {
3160 $line = new SupplierInvoiceLine($this->db);
3161 $line->desc = $langs->trans("Description")." ".$xnbp;
3162 $line->qty = 1;
3163 $line->subprice = 100;
3164 $line->price = 100;
3165 $line->tva_tx = 19.6;
3166 $line->localtax1_tx = 0;
3167 $line->localtax2_tx = 0;
3168 if ($xnbp == 2) {
3169 $line->total_ht = 50;
3170 $line->total_ttc = 59.8;
3171 $line->total_tva = 9.8;
3172 $line->remise_percent = 50;
3173 } else {
3174 $line->total_ht = 100;
3175 $line->total_ttc = 119.6;
3176 $line->total_tva = 19.6;
3177 $line->remise_percent = 0;
3178 }
3179
3180 if ($num_prods > 0) {
3181 $prodid = mt_rand(1, $num_prods);
3182 $line->fk_product = $prodids[$prodid];
3183 }
3184 $line->product_type = 0;
3185
3186 $this->lines[$xnbp] = $line;
3187
3188 $this->total_ht += $line->total_ht;
3189 $this->total_tva += $line->total_tva;
3190 $this->total_ttc += $line->total_ttc;
3191
3192 $xnbp++;
3193 }
3194 }
3195
3196 $this->total_ht = $xnbp * 100;
3197 $this->total_tva = $xnbp * 19.6;
3198 $this->total_ttc = $xnbp * 119.6;
3199
3200 return 1;
3201 }
3202
3208 public function loadStateBoard()
3209 {
3210 global $conf, $user;
3211
3212 $this->nb = array();
3213
3214 $clause = "WHERE";
3215
3216 $sql = "SELECT count(f.rowid) as nb";
3217 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3218 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3219 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3220 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3221 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3222 $clause = "AND";
3223 }
3224 $sql .= " ".$clause." f.entity = ".((int) $conf->entity);
3225
3226 $resql = $this->db->query($sql);
3227 if ($resql) {
3228 while ($obj = $this->db->fetch_object($resql)) {
3229 $this->nb["supplier_invoices"] = $obj->nb;
3230 }
3231 $this->db->free($resql);
3232 return 1;
3233 } else {
3234 dol_print_error($this->db);
3235 $this->error = $this->db->error();
3236 return -1;
3237 }
3238 }
3239
3248 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3249 {
3250 global $conf, $langs, $hookmanager;
3251
3252 $error = 0;
3253
3254 $object = new FactureFournisseur($this->db);
3255
3256 $this->db->begin();
3257
3258 // Load source object
3259 $object->fetch($fromid);
3260 $objFrom = clone $object;
3261
3262 $object->id = 0;
3263 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3264 $object->status = self::STATUS_DRAFT;
3265
3266 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3267
3268 // Clear fields
3269 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3270 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3271 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3272 $object->fk_facture_source = 0;
3273 $object->date_creation = '';
3274 $object->date_validation = '';
3275 $object->date = (empty($this->date) ? dol_now() : $this->date);
3276 $object->ref_client = '';
3277 $object->close_code = '';
3278 $object->close_note = '';
3279 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3280 $object->note_private = '';
3281 $object->note_public = '';
3282 }
3283
3284 $object->date_echeance = $object->calculate_date_lim_reglement();
3285
3286 // Loop on each line of new invoice
3287 foreach ($object->lines as $i => $line) {
3288 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3289 unset($object->lines[$i]);
3290 }
3291 }
3292
3293 // Create clone
3294 $object->context['createfromclone'] = 'createfromclone';
3295 $result = $object->create($user);
3296
3297 // Other options
3298 if ($result < 0) {
3299 $this->error = $object->error;
3300 $this->errors = $object->errors;
3301 $error++;
3302 }
3303
3304 if (!$error) {
3305 // Hook of thirdparty module
3306 if (is_object($hookmanager)) {
3307 $parameters = array('objFrom' => $objFrom);
3308 $action = '';
3309 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
3310 if ($reshook < 0) {
3311 $this->setErrorsFromObject($hookmanager);
3312 $error++;
3313 }
3314 }
3315 }
3316
3317 unset($object->context['createfromclone']);
3318
3319 // End
3320 if (!$error) {
3321 $this->db->commit();
3322 return $object->id;
3323 } else {
3324 $this->db->rollback();
3325 return -1;
3326 }
3327 }
3328
3340 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3341 {
3342 global $langs;
3343
3344 $langs->load("suppliers");
3345 $outputlangs->load("products");
3346
3347 // Set the model on the model name to use
3348 if (empty($modele)) {
3349 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3350 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3351 } else {
3352 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3353 }
3354 } elseif ($modele == 'auto') {
3355 $modele = 'canelle';
3356 }
3357
3358 if (empty($modele)) {
3359 return 0;
3360 } else {
3361 $modelpath = "core/modules/supplier_invoice/doc/";
3362
3363 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3364 }
3365 }
3366
3371 public function getRights()
3372 {
3373 global $user;
3374
3375 return $user->hasRight("fournisseur", "facture");
3376 }
3377
3386 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3387 {
3388 $tables = array(
3389 'facture_fourn'
3390 );
3391
3392 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3393 }
3394
3403 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3404 {
3405 $tables = array(
3406 'facture_fourn_det'
3407 );
3408
3409 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3410 }
3411
3417 public function hasDelay()
3418 {
3419 global $conf;
3420
3421 $now = dol_now();
3422
3423 if (!$this->date_echeance) {
3424 return false;
3425 }
3426
3427 $status = isset($this->status) ? $this->status : $this->statut;
3428
3429 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3430 }
3431
3437 public function isCreditNoteUsed()
3438 {
3439 $isUsed = false;
3440
3441 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3442 $resql = $this->db->query($sql);
3443 if (!empty($resql)) {
3444 $obj = $this->db->fetch_object($resql);
3445 if (!empty($obj->fk_invoice_supplier)) {
3446 $isUsed = true;
3447 }
3448 }
3449
3450 return $isUsed;
3451 }
3459 public function getKanbanView($option = '', $arraydata = null)
3460 {
3461 global $langs;
3462
3463 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3464
3465 $picto = $this->picto;
3466 if ($this->type == self::TYPE_REPLACEMENT) {
3467 $picto .= 'r'; // Replacement invoice
3468 }
3469 if ($this->type == self::TYPE_CREDIT_NOTE) {
3470 $picto .= 'a'; // Credit note
3471 }
3472 if ($this->type == self::TYPE_DEPOSIT) {
3473 $picto .= 'd'; // Deposit invoice
3474 }
3475
3476 $return = '<div class="box-flex-item box-flex-grow-zero">';
3477 $return .= '<div class="info-box info-box-sm">';
3478 $return .= '<span class="info-box-icon bg-infobox-action">';
3479 $return .= img_picto('', $picto);
3480 $return .= '</span>';
3481 $return .= '<div class="info-box-content">';
3482 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . $this->getNomUrl(1) . '</span>';
3483 if ($selected >= 0) {
3484 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3485 }
3486 if (!empty($arraydata['thirdparty'])) {
3487 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3488 }
3489 if (!empty($this->date)) {
3490 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3491 }
3492 if (!empty($this->total_ht)) {
3493 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3494 $return .= ' '.$langs->trans("HT");
3495 $return .= '</span>';
3496 }
3497 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3498 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3499
3500 $return .= '</div>';
3501 $return .= '</div>';
3502 $return .= '</div>';
3503
3504 return $return;
3505 }
3506
3513 public function setVATReverseCharge($vatreversecharge)
3514 {
3515 if (!$this->table_element) {
3516 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3517 return -1;
3518 }
3519
3520 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3521
3522 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3523 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3524 $sql .= " WHERE rowid=".((int) $this->id);
3525
3526 if ($this->db->query($sql)) {
3527 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3528 return 1;
3529 } else {
3530 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3531 $this->error = $this->db->error();
3532 return 0;
3533 }
3534 }
3535
3547 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3548 {
3549 global $conf, $langs, $user;
3550
3551 $this->output = '';
3552 $this->error = '';
3553 $nbMailSend = 0;
3554
3555 $error = 0;
3556 $errorsMsg = array();
3557
3558 $langs->load('bills');
3559
3560 if (!isModEnabled(!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3561 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3562 return 0;
3563 }
3564 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3565 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3566 return 0;
3567 }
3568
3569 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3570 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3571 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3572 $formmail = new FormMail($this->db);
3573
3574 $now = dol_now();
3575 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3576
3577 $tmpinvoice = new FactureFournisseur($this->db);
3578
3579 dol_syslog(__METHOD__." start", LOG_INFO);
3580
3581 // Select all action comm reminder
3582 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3583 if (!empty($paymentmode) && $paymentmode != 'all') {
3584 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3585 }
3586 $sql .= " WHERE f.paye = 0"; // Only unpaid
3587 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3588 if ($datetouse == 'invoicedate') {
3589 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3590 } else {
3591 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3592 }
3593 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3594 if (!empty($paymentmode) && $paymentmode != 'all') {
3595 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3596 }
3597 // TODO Add a filter to check there is no payment started yet
3598 if ($datetouse == 'invoicedate') {
3599 $sql .= $this->db->order("datef", "ASC");
3600 } else {
3601 $sql .= $this->db->order("date_lim_reglement", "ASC");
3602 }
3603
3604 $resql = $this->db->query($sql);
3605
3606 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3607 if ($datetouse == 'invoicedate') {
3608 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3609 } else {
3610 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3611 }
3612 if (!empty($paymentmode) && $paymentmode != 'all') {
3613 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3614 }
3615 $this->output .= '<br>';
3616
3617 if ($resql) {
3618 while ($obj = $this->db->fetch_object($resql)) {
3619 if (!$error) {
3620 // Load event
3621 $res = $tmpinvoice->fetch($obj->id);
3622 if ($res > 0) {
3623 $tmpinvoice->fetch_thirdparty();
3624
3625 $outputlangs = new Translate('', $conf);
3626 if ($tmpinvoice->thirdparty->default_lang) {
3627 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3628 $outputlangs->loadLangs(array("main", "suppliers"));
3629 } else {
3630 $outputlangs = $langs;
3631 }
3632
3633 // Select email template according to language of recipient
3634 $templateId = 0;
3635 $templateLabel = '';
3636 if (empty($template) || $template == 'EmailTemplateCode') {
3637 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3638 } else {
3639 if (is_numeric($template)) {
3640 $templateId = $template;
3641 } else {
3642 $templateLabel = $template;
3643 }
3644 }
3645
3646 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3647 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3648 $langs->load("errors");
3649 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3650 return 0;
3651 }
3652
3653 // PREPARE EMAIL
3654 $errormesg = '';
3655
3656 // Make substitution in email content
3657 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $tmpinvoice);
3658
3659 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3660
3661 // Topic
3662 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3663
3664 // Content
3665 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3666
3667 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3668
3669 // Recipient
3670 $to = array();
3671 if ($forcerecipient) { // If a recipient was forced
3672 $to = array($forcerecipient);
3673 } else {
3674 $res = $tmpinvoice->fetch_thirdparty();
3675 $recipient = $tmpinvoice->thirdparty;
3676 if ($res > 0) {
3677 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3678 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3679 foreach ($tmparraycontact as $data_email) {
3680 if (!empty($data_email['email'])) {
3681 $to[] = $data_email['email'];
3682 }
3683 }
3684 }
3685 if (empty($to) && !empty($recipient->email)) {
3686 $to[] = $recipient->email;
3687 }
3688 if (empty($to)) {
3689 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3690 $error++;
3691 }
3692 } else {
3693 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3694 $error++;
3695 }
3696 }
3697
3698 // Sender
3699 $email_from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3700 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3701 $email_from = (string) $arraymessage->email_from;
3702 }
3703 if (empty($email_from)) {
3704 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3705 $error++;
3706 }
3707
3708 if (!$error && !empty($to)) {
3709 $this->db->begin();
3710
3711 $to = implode(',', $to);
3712 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3713 $to = $to.','.$arraymessage->email_to;
3714 }
3715
3716 // Errors Recipient
3717 $errors_to = getDolGlobalString('MAIN_MAIL_ERRORS_TO');
3718
3719 $trackid = 'inv'.$tmpinvoice->id;
3720 $sendcontext = 'standard';
3721
3722 $email_tocc = '';
3723 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3724 $email_tocc = (string) $arraymessage->email_tocc;
3725 }
3726
3727 $email_tobcc = '';
3728 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3729 $email_tobcc = (string) $arraymessage->email_tobcc;
3730 }
3731
3732 // Mail Creation
3733 $cMailFile = new CMailFile($sendTopic, $to, $email_from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3734
3735 // Sending Mail
3736 if ($cMailFile->sendfile()) {
3737 $nbMailSend++;
3738
3739 // Add a line into event table
3740 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3741
3742 // Insert record of emails sent
3743 $actioncomm = new ActionComm($this->db);
3744
3745 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3746 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3747 $actioncomm->contact_id = 0;
3748
3749 $actioncomm->code = 'AC_EMAIL';
3750 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3751 $actioncomm->note_private = $sendContent;
3752 $actioncomm->fk_project = $tmpinvoice->fk_project;
3753 $actioncomm->datep = dol_now();
3754 $actioncomm->datef = $actioncomm->datep;
3755 $actioncomm->percentage = -1; // Not applicable
3756 $actioncomm->authorid = $user->id; // User saving action
3757 $actioncomm->userownerid = $user->id; // Owner of action
3758 // Fields when action is an email (content should be added into note)
3759 $actioncomm->email_msgid = $cMailFile->msgid;
3760 $actioncomm->email_subject = $sendTopic;
3761 $actioncomm->email_from = $email_from;
3762 $actioncomm->email_sender = '';
3763 $actioncomm->email_to = $to;
3764 //$actioncomm->email_tocc = $sendtocc;
3765 //$actioncomm->email_tobcc = $sendtobcc;
3766 //$actioncomm->email_subject = $subject;
3767 $actioncomm->errors_to = $errors_to;
3768
3769 $actioncomm->elementtype = 'invoice_supplier';
3770 $actioncomm->elementid = $tmpinvoice->id;
3771 $actioncomm->fk_element = $tmpinvoice->id;
3772
3773 //$actioncomm->extraparams = $extraparams;
3774
3775 $actioncomm->create($user);
3776 } else {
3777 $errormesg = $cMailFile->error.' : '.$to;
3778 $error++;
3779
3780 // Add a line into event table
3781 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3782
3783 // Insert record of emails sent
3784 $actioncomm = new ActionComm($this->db);
3785
3786 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3787 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3788 $actioncomm->contact_id = 0;
3789
3790 $actioncomm->code = 'AC_EMAIL';
3791 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3792 $actioncomm->note_private = $errormesg;
3793 $actioncomm->fk_project = $tmpinvoice->fk_project;
3794 $actioncomm->datep = dol_now();
3795 $actioncomm->datef = $actioncomm->datep;
3796 $actioncomm->percentage = -1; // Not applicable
3797 $actioncomm->authorid = $user->id; // User saving action
3798 $actioncomm->userownerid = $user->id; // Owner of action
3799 // Fields when action is an email (content should be added into note)
3800 $actioncomm->email_msgid = $cMailFile->msgid;
3801 $actioncomm->email_from = $email_from;
3802 $actioncomm->email_sender = '';
3803 $actioncomm->email_to = $to;
3804 //$actioncomm->email_tocc = $sendtocc;
3805 //$actioncomm->email_tobcc = $sendtobcc;
3806 //$actioncomm->email_subject = $subject;
3807 $actioncomm->errors_to = $errors_to;
3808
3809 //$actioncomm->extraparams = $extraparams;
3810
3811 $actioncomm->create($user);
3812 }
3813
3814 $this->db->commit(); // We always commit
3815 }
3816
3817 if ($errormesg) {
3818 $errorsMsg[] = $errormesg;
3819 }
3820 } else {
3821 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3822 $error++;
3823 }
3824 }
3825 }
3826 } else {
3827 $error++;
3828 }
3829
3830 if (!$error) {
3831 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3832
3833 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3834
3835 return 0;
3836 } else {
3837 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3838
3839 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3840
3841 return $error;
3842 }
3843 }
3844}
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.
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -> yes (1) I...
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:169
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:665
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)
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
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...
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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, $notused, $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