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