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