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