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