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