dolibarr 21.0.0-alpha
fournisseur.facture.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
8 * Copyright (C) 2013-2019 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2015 Bahfir Abbes <bafbes@gmail.com>
12 * Copyright (C) 2015-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2016-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
14 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
15 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
16 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
17 * Copyright (C) 2023 Nick Fragoulis
18 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <https://www.gnu.org/licenses/>.
32 */
33
40require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
42require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.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
240 public $extraparams = array();
241
246 public $lines = array();
247
252 public $fournisseur;
253
255
258 public $fk_facture_source;
259
261 public $fac_rec;
263 public $fk_fac_rec_source;
264
265 public $fields = array(
266 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
267 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
268 'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20),
269 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1),
270 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30),
271 'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
272 'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36),
273 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 40),
274 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 45),
275 'datef' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 50),
276 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 55),
277 'libelle' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => -1, 'position' => 60),
278 'paye' => array('type' => 'smallint(6)', 'label' => 'Paye', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
279 'amount' => array('type' => 'double(24,8)', 'label' => 'Amount', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
280 'remise' => array('type' => 'double(24,8)', 'label' => 'Discount', 'enabled' => 1, 'visible' => -1, 'position' => 75),
281 'close_code' => array('type' => 'varchar(16)', 'label' => 'CloseCode', 'enabled' => 1, 'visible' => -1, 'position' => 80),
282 'close_note' => array('type' => 'varchar(128)', 'label' => 'CloseNote', 'enabled' => 1, 'visible' => -1, 'position' => 85),
283 'tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 90),
284 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 95),
285 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 100),
286 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 105),
287 'total_tva' => array('type' => 'double(24,8)', 'label' => 'TotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 110),
288 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 115),
289 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -1, 'position' => 125),
290 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 130),
291 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 135),
292 'fk_facture_source' => array('type' => 'integer', 'label' => 'Fk facture source', 'enabled' => 1, 'visible' => -1, 'position' => 140),
293 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 145),
294 'fk_account' => array('type' => 'integer', 'label' => 'Account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
295 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 155),
296 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 160),
297 'date_lim_reglement' => array('type' => 'date', 'label' => 'DateLimReglement', 'enabled' => 1, 'visible' => -1, 'position' => 165),
298 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
299 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
300 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 180),
301 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 190),
302 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => -1, 'position' => 195),
303 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => -1, 'position' => 200),
304 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyId', 'enabled' => 1, 'visible' => -1, 'position' => 205),
305 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCode', 'enabled' => 1, 'visible' => -1, 'position' => 210),
306 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 1, 'visible' => -1, 'position' => 215),
307 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 220),
308 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalVAT', 'enabled' => 1, 'visible' => -1, 'position' => 225),
309 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyTotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 230),
310 'date_pointoftax' => array('type' => 'date', 'label' => 'Date pointoftax', 'enabled' => 1, 'visible' => -1, 'position' => 235),
311 'date_valid' => array('type' => 'date', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 240),
312 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'Last main doc', 'enabled' => 1, 'visible' => -1, 'position' => 245),
313 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
314 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
315 );
316
317
321 const TYPE_STANDARD = 0;
322
327
332
336 const TYPE_DEPOSIT = 3;
337
341 const STATUS_DRAFT = 0;
342
347
355 const STATUS_CLOSED = 2;
356
365
366 const CLOSECODE_DISCOUNTVAT = 'discount_vat';
367 const CLOSECODE_BADCREDIT = 'badsupplier';
368 const CLOSECODE_ABANDONED = 'abandon';
369 const CLOSECODE_REPLACED = 'replaced';
370
376 public function __construct($db)
377 {
378 $this->db = $db;
379
380 $this->ismultientitymanaged = 1;
381 }
382
389 public function create($user)
390 {
391 global $langs, $conf, $hookmanager;
392
393 $error = 0;
394 $now = dol_now();
395
396 // Clean parameters
397 if (isset($this->ref_supplier)) {
398 $this->ref_supplier = trim($this->ref_supplier);
399 }
400 if (empty($this->type)) {
401 $this->type = self::TYPE_STANDARD;
402 }
403 if (empty($this->date)) {
404 $this->date = $now;
405 }
406
407 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
408 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
409 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
410 } else {
411 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
412 }
413 if (empty($this->fk_multicurrency)) {
414 $this->multicurrency_code = $conf->currency;
415 $this->fk_multicurrency = 0;
416 $this->multicurrency_tx = 1;
417 }
418
419 $this->db->begin();
420
421 // Create invoice from a template recurring invoice
422 if ($this->fac_rec > 0) {
423 $this->fk_fac_rec_source = $this->fac_rec;
424
425 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture-rec.class.php';
426 $_facrec = new FactureFournisseurRec($this->db);
427 $result = $_facrec->fetch($this->fac_rec);
428 $result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds
429
430 // Define some dates
431 if (!empty($_facrec->frequency)) {
432 $originaldatewhen = $_facrec->date_when;
433 $nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency);
434 $previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd');
435 $this->socid = $_facrec->socid;
436 } else {
437 $originaldatewhen = 0;
438 $nextdatewhen = 0;
439 $previousdaynextdatewhen = 0;
440 }
441
442 $this->entity = $_facrec->entity; // Invoice created in same entity than template
443
444 // Fields coming from GUI
445 // @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
446 // set by posted page with $object->xxx = ... and this section should be removed.
447 $this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project;
448 $this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public;
449 $this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private;
450 $this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf;
451 $this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id;
452 $this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id;
453 $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account;
454
455 // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result
456 $this->total_ht = $_facrec->total_ht;
457 $this->total_ttc = $_facrec->total_ttc;
458
459 // Fields always coming from template
460 $this->fk_incoterms = $_facrec->fk_incoterms;
461 $this->location_incoterms = $_facrec->location_incoterms;
462
463 // Clean parameters
464 if (! $this->type) {
465 $this->type = self::TYPE_STANDARD;
466 }
467 if (!empty(GETPOST('ref_supplier'))) {
468 $this->ref_supplier = trim($this->ref_supplier);
469 } else {
470 $this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1));
471 }
472 $this->note_public = trim($this->note_public);
473 $this->note_private = trim($this->note_private);
474 $this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title));
475
476 $this->array_options = $_facrec->array_options;
477
478 if (! $this->mode_reglement_id) {
479 $this->mode_reglement_id = 0;
480 }
481 $this->status = self::STATUS_DRAFT;
482 $this->statut = self::STATUS_DRAFT; // deprecated
483
484 $this->linked_objects = $_facrec->linkedObjectsIds;
485 // We do not add link to template invoice or next invoice will be linked to all generated invoices
486 //$this->linked_objects['facturerec'][0] = $this->fac_rec;
487
488 $forceduedate = $this->calculate_date_lim_reglement();
489
490 // For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
491 if ($_facrec->frequency > 0) {
492 dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
493 if (empty($_facrec->date_when)) {
494 $_facrec->date_when = $now;
495 }
496 $next_date = $_facrec->getNextDate(); // Calculate next date
497 $result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, '');
498 //$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1.
499 $result = $_facrec->setNextDate($next_date, 1);
500 }
501
502 // Define lang of customer
503 $outputlangs = $langs;
504 $newlang = '';
505
506 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) {
507 $newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ...
508 }
509 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) {
510 $newlang = $this->default_lang; // for thirdparty
511 }
512 if (!empty($newlang)) {
513 $outputlangs = new Translate("", $conf);
514 $outputlangs->setDefaultLang($newlang);
515 } // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions)
516 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
517 $substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m');
518 $substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m');
519 $substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m');
520 $substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B');
521 $substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B');
522 $substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B');
523 $substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y');
524 $substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y');
525 $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice
526 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : '';
527 $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : '';
528 $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : '';
529 $substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done;
530 $substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max;
531
532 complete_substitutions_array($substitutionarray, $outputlangs);
533
534 $this->note_public = make_substitutions($this->note_public, $substitutionarray);
535 $this->note_private = make_substitutions($this->note_private, $substitutionarray);
536 }
537
538 // Define due date if not already defined
539 if (!empty($forceduedate)) {
540 $this->date_echeance = $forceduedate;
541 }
542
543 $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
544 $sql .= "ref";
545 $sql .= ", ref_supplier";
546 $sql .= ", ref_ext";
547 $sql .= ", entity";
548 $sql .= ", type";
549 $sql .= ", subtype";
550 $sql .= ", libelle";
551 $sql .= ", fk_soc";
552 $sql .= ", datec";
553 $sql .= ", datef";
554 $sql .= ", vat_reverse_charge";
555 $sql .= ", fk_projet";
556 $sql .= ", fk_cond_reglement";
557 $sql .= ", fk_mode_reglement";
558 $sql .= ", fk_account";
559 $sql .= ", note_private";
560 $sql .= ", note_public";
561 $sql .= ", fk_user_author";
562 $sql .= ", date_lim_reglement";
563 $sql .= ", fk_incoterms, location_incoterms";
564 $sql .= ", fk_multicurrency";
565 $sql .= ", multicurrency_code";
566 $sql .= ", multicurrency_tx";
567 $sql .= ", fk_facture_source";
568 $sql .= ", fk_fac_rec_source";
569 $sql .= ")";
570 $sql .= " VALUES (";
571 $sql .= "'(PROV)'";
572 $sql .= ", '".$this->db->escape($this->ref_supplier)."'";
573 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
574 $sql .= ", ".((int) $conf->entity);
575 $sql .= ", '".$this->db->escape($this->type)."'";
576 $sql .= ", ".((int) $this->subtype);
577 $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
578 $sql .= ", ".((int) $this->socid);
579 $sql .= ", '".$this->db->idate($now)."'";
580 $sql .= ", '".$this->db->idate($this->date)."'";
581 $sql .= ", ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0);
582 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
583 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
584 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
585 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
586 $sql .= ", '".$this->db->escape($this->note_private)."'";
587 $sql .= ", '".$this->db->escape($this->note_public)."'";
588 $sql .= ", ".((int) $user->id).",";
589 $sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
590 $sql .= ", ".(int) $this->fk_incoterms;
591 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
592 $sql .= ", ".(int) $this->fk_multicurrency;
593 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
594 $sql .= ", ".(float) $this->multicurrency_tx;
595 $sql .= ", ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null");
596 $sql .= ", ".(isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL");
597 $sql .= ")";
598
599 dol_syslog(get_class($this)."::create", LOG_DEBUG);
600 $resql = $this->db->query($sql);
601 if ($resql) {
602 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
603
604 // Update ref with new one
605 $this->ref = '(PROV'.$this->id.')';
606 $sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
607
608 dol_syslog(get_class($this)."::create", LOG_DEBUG);
609 $resql = $this->db->query($sql);
610 if (!$resql) {
611 $error++;
612 }
613
614 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
615 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
616 }
617
618 // Add object linked
619 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
620 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
621 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, ...))
622 foreach ($tmp_origin_id as $origin_id) {
623 $ret = $this->add_object_linked($origin, $origin_id);
624 if (!$ret) {
625 dol_print_error($this->db);
626 $error++;
627 }
628 }
629 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
630 $origin_id = $tmp_origin_id;
631 $ret = $this->add_object_linked($origin, $origin_id);
632 if (!$ret) {
633 dol_print_error($this->db);
634 $error++;
635 }
636 }
637 }
638 }
639
640 if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
641 dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
642 foreach ($this->lines as $i => $val) {
643 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
644 $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').')';
645
646 $resql_insert = $this->db->query($sql);
647 if ($resql_insert) {
648 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
649
650 $res = $this->updateline(
651 $idligne,
652 $this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description,
653 $this->lines[$i]->subprice,
654 $this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
655 $this->lines[$i]->localtax1_tx,
656 $this->lines[$i]->localtax2_tx,
657 $this->lines[$i]->qty,
658 $this->lines[$i]->fk_product,
659 'HT',
660 (!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
661 $this->lines[$i]->product_type,
662 $this->lines[$i]->remise_percent,
663 0,
664 $this->lines[$i]->date_start,
665 $this->lines[$i]->date_end,
666 $this->lines[$i]->array_options,
667 $this->lines[$i]->fk_unit,
668 $this->lines[$i]->multicurrency_subprice,
669 $this->lines[$i]->ref_supplier
670 );
671 } else {
672 $this->error = $this->db->lasterror();
673 $this->db->rollback();
674 return -5;
675 }
676 }
677 } elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays
678 dol_syslog("There is ".count($this->lines)." lines that are array lines");
679 foreach ($this->lines as $i => $val) {
680 $line = $this->lines[$i];
681
682 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
683 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
684 if (!is_object($line)) {
685 $line = (object) $line;
686 }
687
688 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
689 $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').')';
690
691 $resql_insert = $this->db->query($sql);
692 if ($resql_insert) {
693 $idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
694
695 $this->updateline(
696 $idligne,
697 $line->desc ? $line->desc : $line->description,
698 $line->subprice,
699 $line->tva_tx,
700 $line->localtax1_tx,
701 $line->localtax2_tx,
702 $line->qty,
703 $line->fk_product,
704 'HT',
705 (!empty($line->info_bits) ? $line->info_bits : ''),
706 $line->product_type,
707 $line->remise_percent,
708 0,
709 $line->date_start,
710 $line->date_end,
711 $line->array_options,
712 $line->fk_unit,
713 $line->multicurrency_subprice,
714 $line->ref_supplier
715 );
716 } else {
717 $this->error = $this->db->lasterror();
718 $this->db->rollback();
719 return -5;
720 }
721 }
722 }
723
724 /*
725 * Insert lines of template invoices
726 */
727 if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) {
728 foreach ($_facrec->lines as $i => $val) {
729 if ($_facrec->lines[$i]->fk_product) {
730 $prod = new Product($this->db);
731 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
732 }
733
734 // For line from template invoice, we use data from template invoice
735 /*
736 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
737 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
738 if (empty($tva_tx)) $tva_npr=0;
739 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
740 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
741 */
742 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
743 $tva_npr = $_facrec->lines[$i]->info_bits;
744 if (empty($tva_tx)) {
745 $tva_npr = 0;
746 }
747 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
748 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
749
750 $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
751 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
752
753 // If buyprice not defined from template invoice, we try to guess the best value
754 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
755 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
756 $producttmp = new ProductFournisseur($this->db);
757 $producttmp->fetch($_facrec->lines[$i]->fk_product);
758
759 // If margin module defined on costprice, we try the costprice
760 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
761 // else we get the best supplier price
762 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
763 $buyprice = $producttmp->cost_price;
764 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
765 $buyprice = $producttmp->pmp;
766 } else {
767 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
768 if ($producttmp->product_fourn_price_id > 0) {
769 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
770 }
771 }
772 }
773 }
774
775 $result_insert = $this->addline(
776 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
777 $_facrec->lines[$i]->pu_ht,
778 $tva_tx,
779 $localtax1_tx,
780 $localtax2_tx,
781 $_facrec->lines[$i]->qty,
782 $_facrec->lines[$i]->fk_product,
783 $_facrec->lines[$i]->remise_percent,
784 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
785 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
786 0,
787 $_facrec->lines[$i]->info_bits,
788 'HT',
789 0,
790 $_facrec->lines[$i]->rang,
791 0,
792 $_facrec->lines[$i]->array_options,
793 $_facrec->lines[$i]->fk_unit,
794 0,
795 0,
796 $_facrec->lines[$i]->ref_supplier,
797 $_facrec->lines[$i]->special_code,
798 0,
799 0
800 );
801 if ($result_insert < 0) {
802 $error++;
803 $this->error = $this->db->error();
804 break;
805 }
806 }
807 }
808
809
810 // Update total price
811 $result = $this->update_price(1);
812 if ($result > 0) {
813 // Actions on extra fields
814 if (!$error) {
815 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
816 if ($result < 0) {
817 $error++;
818 }
819 }
820
821 if (!$error) {
822 // Call trigger
823 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
824 if ($result < 0) {
825 $error++;
826 }
827 // End call triggers
828 }
829
830 if (!$error) {
831 $this->db->commit();
832 return $this->id;
833 } else {
834 $this->db->rollback();
835 return -4;
836 }
837 } else {
838 $this->error = $langs->trans('FailedToUpdatePrice');
839 $this->db->rollback();
840 return -3;
841 }
842 } else {
843 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
844 $this->error = $langs->trans('ErrorRefAlreadyExists');
845 $this->db->rollback();
846 return -1;
847 } else {
848 $this->error = $this->db->lasterror();
849 $this->db->rollback();
850 return -2;
851 }
852 }
853 }
854
863 public function fetch($id = 0, $ref = '', $ref_ext = '')
864 {
865 if (empty($id) && empty($ref) && empty($ref_ext)) {
866 return -1;
867 }
868
869 $sql = "SELECT";
870 $sql .= " t.rowid,";
871 $sql .= " t.ref,";
872 $sql .= " t.ref_supplier,";
873 $sql .= " t.ref_ext,";
874 $sql .= " t.entity,";
875 $sql .= " t.type,";
876 $sql .= " t.subtype,";
877 $sql .= " t.fk_soc,";
878 $sql .= " t.datec,";
879 $sql .= " t.datef,";
880 $sql .= " t.tms,";
881 $sql .= " t.libelle as label,";
882 $sql .= " t.paye,";
883 $sql .= " t.close_code,";
884 $sql .= " t.close_note,";
885 $sql .= " t.tva,";
886 $sql .= " t.localtax1,";
887 $sql .= " t.localtax2,";
888 $sql .= " t.total_ht,";
889 $sql .= " t.total_tva,";
890 $sql .= " t.total_ttc,";
891 $sql .= " t.fk_statut as status,";
892 $sql .= " t.fk_user_author,";
893 $sql .= " t.fk_user_valid,";
894 $sql .= " t.fk_facture_source,";
895 $sql .= " t.vat_reverse_charge,";
896 $sql .= " t.fk_fac_rec_source,";
897 $sql .= " t.fk_projet as fk_project,";
898 $sql .= " t.fk_cond_reglement,";
899 $sql .= " t.fk_account,";
900 $sql .= " t.fk_mode_reglement,";
901 $sql .= " t.date_lim_reglement,";
902 $sql .= " t.note_private,";
903 $sql .= " t.note_public,";
904 $sql .= " t.model_pdf,";
905 $sql .= " t.last_main_doc,";
906 $sql .= " t.import_key,";
907 $sql .= " t.extraparams,";
908 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
909 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
910 $sql .= ' s.nom as socnom, s.rowid as socid,';
911 $sql .= ' t.fk_incoterms, t.location_incoterms,';
912 $sql .= " i.libelle as label_incoterms,";
913 $sql .= ' t.fk_transport_mode,';
914 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
915 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
916 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
917 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
918 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
919 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
920 if ($id) {
921 $sql .= " WHERE t.rowid = ".((int) $id);
922 } else {
923 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
924 if ($ref) {
925 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
926 }
927 if ($ref_ext) {
928 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
929 }
930 }
931
932 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
933 $resql = $this->db->query($sql);
934 if ($resql) {
935 if ($this->db->num_rows($resql)) {
936 $obj = $this->db->fetch_object($resql);
937
938 $this->id = $obj->rowid;
939 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
940
941 $this->ref_supplier = $obj->ref_supplier;
942 $this->ref_ext = $obj->ref_ext;
943 $this->entity = $obj->entity;
944 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
945 $this->subtype = (int) $obj->subtype;
946 $this->socid = $obj->fk_soc;
947 $this->datec = $this->db->jdate($obj->datec);
948 $this->date = $this->db->jdate($obj->datef);
949 //$this->datep = $this->db->jdate($obj->datef);
950 $this->tms = $this->db->jdate($obj->tms);
951 $this->libelle = $obj->label; // deprecated
952 $this->label = $obj->label;
953 $this->paye = $obj->paye;
954 $this->paid = $obj->paye;
955 $this->close_code = $obj->close_code;
956 $this->close_note = $obj->close_note;
957 $this->total_localtax1 = $obj->localtax1;
958 $this->total_localtax2 = $obj->localtax2;
959 $this->total_ht = $obj->total_ht;
960 $this->total_tva = $obj->total_tva;
961 $this->total_ttc = $obj->total_ttc;
962 $this->status = $obj->status;
963 $this->statut = $obj->status; // For backward compatibility
964 $this->fk_statut = $obj->status; // For backward compatibility
965 $this->user_creation_id = $obj->fk_user_author;
966 $this->author = $obj->fk_user_author; // deprecated
967 $this->user_validation_id = $obj->fk_user_valid;
968 $this->fk_facture_source = $obj->fk_facture_source;
969 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
970 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
971 $this->fk_project = $obj->fk_project;
972 $this->cond_reglement_id = $obj->fk_cond_reglement;
973 $this->cond_reglement_code = $obj->cond_reglement_code;
974 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
975 $this->cond_reglement_label = $obj->cond_reglement_label;
976 $this->cond_reglement_doc = $obj->cond_reglement_doc;
977 $this->fk_account = $obj->fk_account;
978 $this->mode_reglement_id = $obj->fk_mode_reglement;
979 $this->mode_reglement_code = $obj->mode_reglement_code;
980 $this->mode_reglement = $obj->mode_reglement_label;
981 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
982 $this->note = $obj->note_private; // deprecated
983 $this->note_private = $obj->note_private;
984 $this->note_public = $obj->note_public;
985 $this->model_pdf = $obj->model_pdf;
986 $this->last_main_doc = $obj->last_main_doc;
987 $this->import_key = $obj->import_key;
988
989 //Incoterms
990 $this->fk_incoterms = $obj->fk_incoterms;
991 $this->location_incoterms = $obj->location_incoterms;
992 $this->label_incoterms = $obj->label_incoterms;
993 $this->transport_mode_id = $obj->fk_transport_mode;
994
995 // Multicurrency
996 $this->fk_multicurrency = $obj->fk_multicurrency;
997 $this->multicurrency_code = $obj->multicurrency_code;
998 $this->multicurrency_tx = $obj->multicurrency_tx;
999 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1000 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1001 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1002
1003 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1004
1005 $this->socid = $obj->socid;
1006
1007 // Retrieve all extrafield
1008 // fetch optionals attributes and labels
1009 $this->fetch_optionals();
1010
1011 $result = $this->fetch_lines();
1012 if ($result < 0) {
1013 $this->error = $this->db->lasterror();
1014 return -3;
1015 }
1016 } else {
1017 $this->error = 'Bill with id '.$id.' not found';
1018 dol_syslog(get_class($this).'::fetch '.$this->error);
1019 return 0;
1020 }
1021
1022 $this->db->free($resql);
1023 return 1;
1024 } else {
1025 $this->error = "Error ".$this->db->lasterror();
1026 return -1;
1027 }
1028 }
1029
1030
1031 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1037 public function fetch_lines()
1038 {
1039 // phpcs:enable
1040 $this->lines = array();
1041
1042 $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';
1043 $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';
1044 $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';
1045 $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';
1046 $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';
1047 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1048 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1049 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1050 $sql .= ' ORDER BY f.rang, f.rowid';
1051
1052 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1053
1054 $resql_rows = $this->db->query($sql);
1055 if ($resql_rows) {
1056 $num_rows = $this->db->num_rows($resql_rows);
1057 if ($num_rows) {
1058 $i = 0;
1059 while ($i < $num_rows) {
1060 $obj = $this->db->fetch_object($resql_rows);
1061
1062 $line = new SupplierInvoiceLine($this->db);
1063
1064 $line->id = $obj->rowid;
1065 $line->rowid = $obj->rowid;
1066 $line->description = $obj->line_desc;
1067 $line->desc = $obj->line_desc;
1068 $line->date_start = $obj->date_start;
1069 $line->date_end = $obj->date_end;
1070 $line->product_ref = $obj->product_ref;
1071 $line->ref = $obj->product_ref;
1072 $line->ref_supplier = $obj->ref_supplier;
1073 $line->libelle = $obj->label;
1074 $line->label = $obj->label;
1075 $line->product_barcode = $obj->product_barcode;
1076 $line->product_desc = $obj->product_desc;
1077 $line->subprice = $obj->pu_ht;
1078 $line->pu_ht = $obj->pu_ht;
1079 $line->pu_ttc = $obj->pu_ttc;
1080 $line->vat_src_code = $obj->vat_src_code;
1081 $line->tva_tx = $obj->tva_tx;
1082 $line->localtax1_tx = $obj->localtax1_tx;
1083 $line->localtax2_tx = $obj->localtax2_tx;
1084 $line->localtax1_type = $obj->localtax1_type;
1085 $line->localtax2_type = $obj->localtax2_type;
1086 $line->qty = $obj->qty;
1087 $line->remise_percent = $obj->remise_percent;
1088 $line->fk_remise_except = $obj->fk_remise_except;
1089 //$line->tva = $obj->total_tva; // deprecated
1090 $line->total_ht = $obj->total_ht;
1091 $line->total_ttc = $obj->total_ttc;
1092 $line->total_tva = $obj->total_tva;
1093 $line->total_localtax1 = $obj->total_localtax1;
1094 $line->total_localtax2 = $obj->total_localtax2;
1095 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1096 $line->fk_product = $obj->fk_product;
1097 $line->product_type = $obj->product_type;
1098 $line->product_label = $obj->label;
1099 $line->info_bits = $obj->info_bits;
1100 $line->fk_parent_line = $obj->fk_parent_line;
1101 $line->special_code = $obj->special_code;
1102 $line->rang = $obj->rang;
1103 $line->fk_unit = $obj->fk_unit;
1104
1105 // Accountancy
1106 $line->fk_accounting_account = $obj->fk_code_ventilation;
1107
1108 // Multicurrency
1109 $line->fk_multicurrency = $obj->fk_multicurrency;
1110 $line->multicurrency_code = $obj->multicurrency_code;
1111 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1112 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1113 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1114 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1115
1116 // Extra fields
1117 $line->fetch_optionals();
1118
1119 $this->lines[$i] = $line;
1120
1121 $i++;
1122 }
1123 }
1124 $this->db->free($resql_rows);
1125 return 1;
1126 } else {
1127 $this->error = $this->db->error();
1128 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1129 return -3;
1130 }
1131 }
1132
1133
1141 public function update($user = null, $notrigger = 0)
1142 {
1143 global $langs;
1144 $error = 0;
1145
1146 // Clean parameters
1147 if (empty($this->type)) {
1148 $this->type = self::TYPE_STANDARD;
1149 }
1150 if (isset($this->ref)) {
1151 $this->ref = trim($this->ref);
1152 }
1153 if (isset($this->ref_supplier)) {
1154 $this->ref_supplier = trim($this->ref_supplier);
1155 }
1156 if (isset($this->ref_ext)) {
1157 $this->ref_ext = trim($this->ref_ext);
1158 }
1159 if (isset($this->entity)) {
1160 $this->entity = (int) $this->entity;
1161 }
1162 if (isset($this->type)) {
1163 $this->type = (int) $this->type;
1164 }
1165 if (isset($this->subtype)) {
1166 $this->subtype = (int) $this->subtype;
1167 }
1168 if (isset($this->socid)) {
1169 $this->socid = (int) $this->socid;
1170 }
1171 if (isset($this->label)) {
1172 $this->label = trim($this->label);
1173 }
1174 if (isset($this->paid)) {
1175 $this->paid = (int) (bool) $this->paye;
1176 $this->paye = $this->paid;
1177 } elseif (isset($this->paye)) {
1178 $this->paid = (int) (bool) $this->paye;
1179 $this->paye = $this->paid;
1180 }
1181 if (isset($this->close_code)) {
1182 $this->close_code = trim($this->close_code);
1183 }
1184 if (isset($this->close_note)) {
1185 $this->close_note = trim($this->close_note);
1186 }
1187 if (empty($this->total_ht)) {
1188 $this->total_ht = 0;
1189 }
1190 if (empty($this->total_tva)) {
1191 $this->total_tva = 0;
1192 }
1193 if (isset($this->total_ttc)) {
1194 $this->total_ttc = (float) $this->total_ttc;
1195 }
1196 if (isset($this->status)) {
1197 $this->status = (int) $this->status;
1198 $this->statut = $this->status;
1199 } elseif (isset($this->statut)) {
1200 $this->status = (int) $this->statut;
1201 $this->statut = $this->status;
1202 }
1203 if (isset($this->author)) { // TODO: user_creation_id?
1204 $this->author = (int) $this->author;
1205 }
1206 if (isset($this->fk_user_valid)) {
1207 $this->fk_user_valid = trim($this->fk_user_valid);
1208 }
1209 if (isset($this->fk_facture_source)) {
1210 $this->fk_facture_source = (int) $this->fk_facture_source;
1211 }
1212 if (isset($this->fk_project)) {
1213 if (empty($this->fk_project)) {
1214 $this->fk_project = 0;
1215 } else {
1216 $this->fk_project = (int) $this->fk_project;
1217 }
1218 }
1219 if (isset($this->cond_reglement_id)) {
1220 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1221 }
1222 if (isset($this->note_private)) {
1223 $this->note_private = trim($this->note_private);
1224 $this->note = $this->note_private;
1225 }
1226 if (isset($this->note_public)) {
1227 $this->note_public = trim($this->note_public);
1228 }
1229 if (isset($this->model_pdf)) {
1230 $this->model_pdf = trim($this->model_pdf);
1231 }
1232 if (isset($this->import_key)) {
1233 $this->import_key = trim($this->import_key);
1234 }
1235
1236
1237 // Check parameters
1238 // Put here code to add control on parameters values
1239
1240 // Update request
1241 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1242 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1243 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1244 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1245 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1246 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1247 $sql .= " subtype=".((int) $this->subtype).",";
1248 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1249 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1250 $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1251 if (dol_strlen($this->tms) != 0) {
1252 $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1253 }
1254 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1255 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1256 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1257 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1258 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1259 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1260 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1261 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1262 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1263 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1264 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1265 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1266 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1267 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1268 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1269 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1270 $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1271 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1272 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1273 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1274 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1275 $sql .= " WHERE rowid=".((int) $this->id);
1276
1277 $this->db->begin();
1278
1279 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1280 $resql = $this->db->query($sql);
1281
1282 if (!$resql) {
1283 $error++;
1284
1285 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1286 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1287 } else {
1288 $this->errors[] = "Error ".$this->db->lasterror();
1289 }
1290 }
1291
1292 if (!$error) {
1293 $result = $this->insertExtraFields();
1294 if ($result < 0) {
1295 $error++;
1296 }
1297 }
1298
1299 if (!$error) {
1300 if (!$notrigger) {
1301 // Call trigger
1302 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1303 if ($result < 0) {
1304 $error++;
1305 }
1306 // End call triggers
1307 }
1308 }
1309
1310 // Commit or rollback
1311 if ($error) {
1312 foreach ($this->errors as $errmsg) {
1313 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1314 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1315 }
1316 $this->db->rollback();
1317 return -1 * $error;
1318 } else {
1319 $this->db->commit();
1320 return 1;
1321 }
1322 }
1323
1324 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1331 public function insert_discount($idremise)
1332 {
1333 // phpcs:enable
1334 global $conf, $langs;
1335
1336 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1337 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1338
1339 $this->db->begin();
1340
1341 $remise = new DiscountAbsolute($this->db);
1342 $result = $remise->fetch($idremise);
1343
1344 if ($result > 0) {
1345 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1346 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1347 $this->db->rollback();
1348 return -5;
1349 }
1350
1351 $facligne = new SupplierInvoiceLine($this->db);
1352 $facligne->fk_facture_fourn = $this->id;
1353 $facligne->fk_remise_except = $remise->id;
1354 $facligne->desc = $remise->description; // Description ligne
1355 $facligne->vat_src_code = $remise->vat_src_code;
1356 $facligne->tva_tx = $remise->tva_tx;
1357 $facligne->subprice = -$remise->amount_ht;
1358 $facligne->fk_product = 0; // Id produit predefini
1359 $facligne->product_type = 0;
1360 $facligne->qty = 1;
1361 $facligne->remise_percent = 0;
1362 $facligne->rang = -1;
1363 $facligne->info_bits = 2;
1364
1365 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1366 $facligne->rang = 1;
1367 $linecount = count($this->lines);
1368 for ($ii = 1; $ii <= $linecount; $ii++) {
1369 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1370 }
1371 }
1372
1373 // Get buy/cost price of invoice that is source of discount
1374 if ($remise->fk_invoice_supplier_source > 0) {
1375 $srcinvoice = new FactureFournisseur($this->db);
1376 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1377 $totalcostpriceofinvoice = 0;
1378 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1379 $formmargin = new FormMargin($this->db);
1380 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1381 $facligne->pa_ht = $arraytmp['pa_total'];
1382 }
1383
1384 $facligne->total_ht = -$remise->amount_ht;
1385 $facligne->total_tva = -$remise->amount_tva;
1386 $facligne->total_ttc = -$remise->amount_ttc;
1387
1388 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1389 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1390 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1391 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1392
1393 $lineid = $facligne->insert();
1394 if ($lineid > 0) {
1395 $result = $this->update_price(1);
1396 if ($result > 0) {
1397 // Create link between discount and invoice line
1398 $result = $remise->link_to_invoice($lineid, 0);
1399 if ($result < 0) {
1400 $this->error = $remise->error;
1401 $this->db->rollback();
1402 return -4;
1403 }
1404
1405 $this->db->commit();
1406 return 1;
1407 } else {
1408 $this->error = $facligne->error;
1409 $this->db->rollback();
1410 return -1;
1411 }
1412 } else {
1413 $this->error = $facligne->error;
1414 $this->db->rollback();
1415 return -2;
1416 }
1417 } else {
1418 $this->db->rollback();
1419 return -3;
1420 }
1421 }
1422
1423
1431 public function delete(User $user, $notrigger = 0)
1432 {
1433 global $conf;
1434
1435 $rowid = $this->id;
1436
1437 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1438
1439 // TODO Test if there is at least on payment. If yes, refuse to delete.
1440
1441 $error = 0;
1442 $this->db->begin();
1443
1444 if (!$error && !$notrigger) {
1445 // Call trigger
1446 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1447 if ($result < 0) {
1448 $this->db->rollback();
1449 return -1;
1450 }
1451 // Fin appel triggers
1452 }
1453
1454 if (!$error) {
1455 // If invoice was converted into a discount not yet consumed, we remove discount
1456 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1457 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1458 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1459 $resql = $this->db->query($sql);
1460
1461 // If invoice has consumned discounts
1462 $this->fetch_lines();
1463 $list_rowid_det = array();
1464 foreach ($this->lines as $key => $invoiceline) {
1465 $list_rowid_det[] = $invoiceline->id;
1466 }
1467
1468 // Consumned discounts are freed
1469 if (count($list_rowid_det)) {
1470 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1471 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1472 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1473
1474 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1475 if (!$this->db->query($sql)) {
1476 $error++;
1477 }
1478 }
1479 }
1480
1481 if (!$error) {
1482 $main = MAIN_DB_PREFIX.'facture_fourn_det';
1483 $ef = $main."_extrafields";
1484 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1485 $resqlef = $this->db->query($sqlef);
1486 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1487 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1488 $resql = $this->db->query($sql);
1489 if ($resqlef && $resql) {
1490 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1491 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1492 $resql2 = $this->db->query($sql);
1493 if (!$resql2) {
1494 $error++;
1495 }
1496 } else {
1497 $error++;
1498 }
1499 }
1500
1501 if (!$error) {
1502 // Delete linked object
1503 $res = $this->deleteObjectLinked();
1504 if ($res < 0) {
1505 $error++;
1506 }
1507 }
1508
1509 if (!$error) {
1510 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1511 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1512 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1513
1514 // We remove directory
1515 if ($conf->fournisseur->facture->dir_output) {
1516 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1517
1518 $ref = dol_sanitizeFileName($this->ref);
1519 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1520 $file = $dir."/".$ref.".pdf";
1521 if (file_exists($file)) {
1522 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1523 $this->error = 'ErrorFailToDeleteFile';
1524 $error++;
1525 }
1526 }
1527 if (file_exists($dir)) {
1528 $res = @dol_delete_dir_recursive($dir);
1529
1530 if (!$res) {
1531 $this->error = 'ErrorFailToDeleteDir';
1532 $error++;
1533 }
1534 }
1535 }
1536 }
1537
1538 // Remove extrafields
1539 if (!$error) {
1540 $result = $this->deleteExtraFields();
1541 if ($result < 0) {
1542 $error++;
1543 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1544 }
1545 }
1546
1547 if (!$error) {
1548 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1549 $this->db->commit();
1550 return 1;
1551 } else {
1552 $this->error = $this->db->lasterror();
1553 $this->db->rollback();
1554 return -$error;
1555 }
1556 }
1557
1558
1559 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1570 public function set_paid($user, $close_code = '', $close_note = '')
1571 {
1572 // phpcs:enable
1573 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1574 return $this->setPaid($user, $close_code, $close_note);
1575 }
1576
1585 public function setPaid($user, $close_code = '', $close_note = '')
1586 {
1587 $error = 0;
1588
1589 if ($this->paid != 1) {
1590 $this->db->begin();
1591
1592 $now = dol_now();
1593
1594 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1595
1596 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1597 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1598 if (!$close_code) {
1599 $sql .= ', paye=1';
1600 }
1601 if ($close_code) {
1602 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1603 }
1604 if ($close_note) {
1605 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1606 }
1607 $sql .= ', fk_user_closing = '.((int) $user->id);
1608 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1609 $sql .= ' WHERE rowid = '.((int) $this->id);
1610
1611 $resql = $this->db->query($sql);
1612 if ($resql) {
1613 // Call trigger
1614 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1615 if ($result < 0) {
1616 $error++;
1617 }
1618 // End call triggers
1619 } else {
1620 $error++;
1621 $this->error = $this->db->error();
1622 dol_print_error($this->db);
1623 }
1624
1625 if (!$error) {
1626 $this->db->commit();
1627 return 1;
1628 } else {
1629 $this->db->rollback();
1630 return -1;
1631 }
1632 } else {
1633 return 0;
1634 }
1635 }
1636
1637 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1648 public function set_unpaid($user)
1649 {
1650 // phpcs:enable
1651 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1652 return $this->setUnpaid($user);
1653 }
1654
1663 public function setUnpaid($user)
1664 {
1665 $error = 0;
1666
1667 $this->db->begin();
1668
1669 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1670 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1671 $sql .= ' date_closing=null,';
1672 $sql .= ' fk_user_closing=null';
1673 $sql .= ' WHERE rowid = '.((int) $this->id);
1674
1675 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1676 $resql = $this->db->query($sql);
1677 if ($resql) {
1678 // Call trigger
1679 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1680 if ($result < 0) {
1681 $error++;
1682 }
1683 // End call triggers
1684 } else {
1685 $error++;
1686 $this->error = $this->db->error();
1687 dol_print_error($this->db);
1688 }
1689
1690 if (!$error) {
1691 $this->db->commit();
1692 return 1;
1693 } else {
1694 $this->db->rollback();
1695 return -1;
1696 }
1697 }
1698
1709 public function setCanceled($user, $close_code = '', $close_note = '')
1710 {
1711 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1712
1713 $this->db->begin();
1714
1715 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1716 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1717 if ($close_code) {
1718 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1719 }
1720 if ($close_note) {
1721 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1722 }
1723 $sql .= " WHERE rowid = ".((int) $this->id);
1724
1725 $resql = $this->db->query($sql);
1726 if ($resql) {
1727 // Bound discounts are deducted from the invoice
1728 // as they have not been used since the invoice is abandoned.
1729 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1730 $sql .= ' SET fk_invoice_supplier = NULL';
1731 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1732
1733 $resql = $this->db->query($sql);
1734 if ($resql) {
1735 // Call trigger
1736 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1737 if ($result < 0) {
1738 $this->db->rollback();
1739 return -1;
1740 }
1741 // End call triggers
1742
1743 $this->db->commit();
1744 return 1;
1745 } else {
1746 $this->error = $this->db->error()." sql=".$sql;
1747 $this->db->rollback();
1748 return -1;
1749 }
1750 } else {
1751 $this->error = $this->db->error()." sql=".$sql;
1752 $this->db->rollback();
1753 return -2;
1754 }
1755 }
1756
1766 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1767 {
1768 global $mysoc, $conf, $langs;
1769
1770 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1771
1772 $now = dol_now();
1773
1774 $error = 0;
1775 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1776
1777 // Force to have object complete for checks
1778 $this->fetch_thirdparty();
1779 $this->fetch_lines();
1780
1781 // Check parameters
1782 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1783 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1784 return 0;
1785 }
1786 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1787 $langs->load("errors");
1788 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1789 return -1;
1790 }
1791 if (count($this->lines) <= 0) {
1792 $langs->load("errors");
1793 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1794 return -1;
1795 }
1796
1797 // Check for mandatory fields in thirdparty (defined into setup)
1798 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1799 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1800 foreach ($array_to_check as $key) {
1801 $keymin = strtolower($key);
1802 if ($keymin == 'accountancy_code_supplier') {
1803 $keymin = 'code_compta_fournisseur';
1804 }
1805 if (!property_exists($this->thirdparty, $keymin)) {
1806 continue;
1807 }
1808 $vallabel = $this->thirdparty->$keymin;
1809
1810 $i = (int) preg_replace('/[^0-9]/', '', $key);
1811 if ($i > 0) {
1812 if ($this->thirdparty->isACompany()) {
1813 // Check for mandatory prof id (but only if country is other than ours)
1814 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1815 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1816 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1817 $langs->load("errors");
1818 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1819 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1820 return -1;
1821 }
1822 }
1823 }
1824 } else {
1825 if ($key == 'EMAIL') {
1826 // Check for mandatory
1827 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1828 $langs->load("errors");
1829 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1830 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1831 return -1;
1832 }
1833 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1834 // Check for mandatory
1835 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1836 $langs->load("errors");
1837 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1838 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1839 return -1;
1840 }
1841 }
1842 }
1843 }
1844 }
1845
1846 $this->db->begin();
1847
1848 // Define new ref
1849 if ($force_number) {
1850 $num = $force_number;
1851 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1852 $num = $this->getNextNumRef($this->thirdparty);
1853 } else {
1854 $num = $this->ref;
1855 }
1856 $this->newref = dol_sanitizeFileName($num);
1857
1858 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1859 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1860 $sql .= " WHERE rowid = ".((int) $this->id);
1861
1862 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1863 $resql = $this->db->query($sql);
1864 if ($resql) {
1865 // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1866 if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1867 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1868 $langs->load("agenda");
1869
1870 $cpt = count($this->lines);
1871 for ($i = 0; $i < $cpt; $i++) {
1872 if ($this->lines[$i]->fk_product > 0) {
1873 $mouvP = new MouvementStock($this->db);
1874 $mouvP->origin = &$this;
1875 $mouvP->setOrigin($this->element, $this->id);
1876 // We increase stock for product
1877 $up_ht_disc = $this->lines[$i]->subprice;
1878 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1879 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1880 }
1882 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1883 } else {
1884 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1885 }
1886 if ($result < 0) {
1887 $this->error = $mouvP->error;
1888 if (count($mouvP->errors)) {
1889 $this->errors = $mouvP->errors;
1890 }
1891 return -2;
1892 }
1893 }
1894 }
1895 }
1896
1897 // Triggers call
1898 if (!$error && empty($notrigger)) {
1899 // Call trigger
1900 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1901 if ($result < 0) {
1902 $error++;
1903 }
1904 // End call triggers
1905 }
1906
1907 if (!$error) {
1908 $this->oldref = $this->ref;
1909
1910 // Rename directory if dir was a temporary ref
1911 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1912 // Now we rename also files into index
1913 $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)."'";
1914 $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;
1915 $resql = $this->db->query($sql);
1916 if (!$resql) {
1917 $error++;
1918 $this->error = $this->db->lasterror();
1919 }
1920 $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)."'";
1921 $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1922 $resql = $this->db->query($sql);
1923 if (!$resql) {
1924 $error++;
1925 $this->error = $this->db->lasterror();
1926 }
1927
1928 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1929 $oldref = dol_sanitizeFileName($this->ref);
1930 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1931 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1932 if (!$error && file_exists($dirsource)) {
1933 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1934
1935 if (@rename($dirsource, $dirdest)) {
1936 dol_syslog("Rename ok");
1937 // Rename docs starting with $oldref with $this->newref
1938 $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, '/'));
1939 foreach ($listoffiles as $fileentry) {
1940 $dirsource = $fileentry['name'];
1941 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1942 $dirsource = $fileentry['path'].'/'.$dirsource;
1943 $dirdest = $fileentry['path'].'/'.$dirdest;
1944 @rename($dirsource, $dirdest);
1945 }
1946 }
1947 }
1948 }
1949 }
1950
1951 // Set new ref and define current statut
1952 if (!$error) {
1953 $this->ref = $this->newref;
1956 //$this->date_validation=$now; this is stored into log table
1957 }
1958
1959 if (!$error) {
1960 $this->db->commit();
1961 return 1;
1962 } else {
1963 $this->db->rollback();
1964 return -1;
1965 }
1966 } else {
1967 $this->error = $this->db->error();
1968 $this->db->rollback();
1969 return -1;
1970 }
1971 }
1972
1981 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
1982 {
1983 // phpcs:enable
1984 global $conf, $langs;
1985
1986 $error = 0;
1987
1988 if ($this->status == self::STATUS_DRAFT) {
1989 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1990 return 0;
1991 }
1992
1993 dol_syslog(__METHOD__, LOG_DEBUG);
1994
1995 $this->db->begin();
1996
1997 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1998 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1999 $sql .= " WHERE rowid = ".((int) $this->id);
2000
2001 $result = $this->db->query($sql);
2002 if ($result) {
2003 if (!$error) {
2004 $this->oldcopy = clone $this;
2005 }
2006
2007 // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
2008 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2009 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2010 $langs->load("agenda");
2011
2012 $cpt = count($this->lines);
2013 for ($i = 0; $i < $cpt; $i++) {
2014 if ($this->lines[$i]->fk_product > 0) {
2015 $mouvP = new MouvementStock($this->db);
2016 $mouvP->origin = &$this;
2017 $mouvP->setOrigin($this->element, $this->id);
2018 // We increase stock for product
2020 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2021 } else {
2022 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2023 }
2024 }
2025 }
2026 }
2027 // Triggers call
2028 if (!$error && empty($notrigger)) {
2029 // Call trigger
2030 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2031 if ($result < 0) {
2032 $error++;
2033 }
2034 // End call triggers
2035 }
2036 if ($error == 0) {
2037 $this->db->commit();
2038 return 1;
2039 } else {
2040 $this->db->rollback();
2041 return -1;
2042 }
2043 } else {
2044 $this->error = $this->db->error();
2045 $this->db->rollback();
2046 return -1;
2047 }
2048 }
2049
2050
2084 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)
2085 {
2086 global $langs, $mysoc;
2087
2088 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);
2089 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2090
2091 if ($this->status == self::STATUS_DRAFT) {
2092 // Clean parameters
2093 if (empty($remise_percent)) {
2094 $remise_percent = 0;
2095 }
2096 if (empty($qty)) {
2097 $qty = 0;
2098 }
2099 if (empty($info_bits)) {
2100 $info_bits = 0;
2101 }
2102 if (empty($rang)) {
2103 $rang = 0;
2104 }
2105 if (empty($fk_code_ventilation)) {
2106 $fk_code_ventilation = 0;
2107 }
2108 if (empty($txtva)) {
2109 $txtva = 0;
2110 }
2111 if (empty($txlocaltax1)) {
2112 $txlocaltax1 = 0;
2113 }
2114 if (empty($txlocaltax2)) {
2115 $txlocaltax2 = 0;
2116 }
2117
2118 $remise_percent = price2num($remise_percent);
2119 $qty = price2num($qty);
2120 $pu = price2num($pu);
2121 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2122 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2123 }
2124 $txlocaltax1 = price2num($txlocaltax1);
2125 $txlocaltax2 = price2num($txlocaltax2);
2126
2127 if ($date_start && $date_end && $date_start > $date_end) {
2128 $langs->load("errors");
2129 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2130 return -1;
2131 }
2132
2133 $this->db->begin();
2134
2135 if ($fk_product > 0) {
2136 if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) {
2137 // Check quantity is enough
2138 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2139 $prod = new ProductFournisseur($this->db);
2140 if ($prod->fetch($fk_product) > 0) {
2141 $product_type = $prod->type;
2142 $label = $prod->label;
2143 $fk_prod_fourn_price = 0;
2144
2145 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2146 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2147 $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
2148 if ($result > 0) {
2149 if (empty($pu)) {
2150 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2151 }
2152 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2153 // is remise percent not keyed but present for the product we add it
2154 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2155 $remise_percent = $prod->remise_percent;
2156 }
2157 }
2158 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2159 $langs->load("errors");
2160 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2161 $this->db->rollback();
2162 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2163 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2164 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2165 return -1;
2166 }
2167 if ($result == -1) {
2168 $langs->load("errors");
2169 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2170 $this->db->rollback();
2171 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2172 return -1;
2173 }
2174 if ($result < -1) {
2175 $this->error = $prod->error;
2176 $this->db->rollback();
2177 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2178 return -1;
2179 }
2180 } else {
2181 $this->error = $prod->error;
2182 $this->db->rollback();
2183 return -1;
2184 }
2185 }
2186 } else {
2187 $product_type = $type;
2188 }
2189
2190 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2191 $pu = 0;
2192 }
2193
2194 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2195
2196 // Clean vat code
2197 $reg = array();
2198 $vat_src_code = '';
2199 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2200 $vat_src_code = $reg[1];
2201 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2202 }
2203
2204 // Calcul du total TTC et de la TVA pour la ligne a partir de
2205 // qty, pu, remise_percent et txtva
2206 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2207 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2208
2209 $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);
2210 $total_ht = $tabprice[0];
2211 $total_tva = $tabprice[1];
2212 $total_ttc = $tabprice[2];
2213 $total_localtax1 = $tabprice[9];
2214 $total_localtax2 = $tabprice[10];
2215 $pu_ht = $tabprice[3];
2216
2217 // MultiCurrency
2218 $multicurrency_total_ht = $tabprice[16];
2219 $multicurrency_total_tva = $tabprice[17];
2220 $multicurrency_total_ttc = $tabprice[18];
2221 $pu_ht_devise = $tabprice[19];
2222
2223 // Check parameters
2224 if ($type < 0) {
2225 return -1;
2226 }
2227
2228 if ($rang < 0) {
2229 $rangmax = $this->line_max();
2230 $rang = $rangmax + 1;
2231 }
2232
2233 // Insert line
2234 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2235
2236 $supplierinvoiceline->context = $this->context;
2237
2238 $supplierinvoiceline->fk_facture_fourn = $this->id;
2239 //$supplierinvoiceline->label=$label; // deprecated
2240 $supplierinvoiceline->desc = $desc;
2241 $supplierinvoiceline->ref_supplier = $ref_supplier;
2242
2243 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2244 $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2245
2246 $supplierinvoiceline->vat_src_code = $vat_src_code;
2247 $supplierinvoiceline->tva_tx = $txtva;
2248 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2249 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2250 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2251 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2252
2253 $supplierinvoiceline->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht); // For credit note and if qty is negative, total is negative
2254 $supplierinvoiceline->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva); // For credit note and if qty is negative, total is negative
2255 $supplierinvoiceline->total_localtax1 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax1) : $total_localtax1); // For credit note and if qty is negative, total is negative
2256 $supplierinvoiceline->total_localtax2 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_localtax2) : $total_localtax2); // For credit note and if qty is negative, total is negative
2257 $supplierinvoiceline->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc); // For credit note and if qty is negative, total is negative
2258
2259 $supplierinvoiceline->fk_product = $fk_product;
2260 $supplierinvoiceline->product_type = $type;
2261 $supplierinvoiceline->remise_percent = $remise_percent;
2262 $supplierinvoiceline->date_start = $date_start;
2263 $supplierinvoiceline->date_end = $date_end;
2264 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2265 $supplierinvoiceline->rang = $rang;
2266 $supplierinvoiceline->info_bits = $info_bits;
2267 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2268
2269
2270 $supplierinvoiceline->special_code = (int) $special_code;
2271 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2272 $supplierinvoiceline->origin = $this->origin;
2273 $supplierinvoiceline->origin_id = $origin_id;
2274 $supplierinvoiceline->fk_unit = $fk_unit;
2275
2276 // Multicurrency
2277 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2278 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2279 $supplierinvoiceline->multicurrency_subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht_devise) : $pu_ht_devise); // For credit note, unit price always negative, always positive otherwise
2280
2281 $supplierinvoiceline->multicurrency_total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ht) : $multicurrency_total_ht); // For credit note and if qty is negative, total is negative
2282 $supplierinvoiceline->multicurrency_total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_tva) : $multicurrency_total_tva); // For credit note and if qty is negative, total is negative
2283 $supplierinvoiceline->multicurrency_total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($multicurrency_total_ttc) : $multicurrency_total_ttc); // For credit note and if qty is negative, total is negative
2284
2285 if (is_array($array_options) && count($array_options) > 0) {
2286 $supplierinvoiceline->array_options = $array_options;
2287 }
2288
2289 $result = $supplierinvoiceline->insert($notrigger);
2290 if ($result > 0) {
2291 // Reorder if child line
2292 if (!empty($fk_parent_line)) {
2293 $this->line_order(true, 'DESC');
2294 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2295 $linecount = count($this->lines);
2296 for ($ii = $rang; $ii <= $linecount; $ii++) {
2297 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2298 }
2299 }
2300
2301 // Mise a jour information denormalisees au niveau de la facture meme
2302 $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.
2303 if ($result > 0) {
2304 $this->db->commit();
2305 return $supplierinvoiceline->id;
2306 } else {
2307 $this->error = $this->db->error();
2308 $this->db->rollback();
2309 return -1;
2310 }
2311 } else {
2312 $this->error = $supplierinvoiceline->error;
2313 $this->errors = $supplierinvoiceline->errors;
2314 $this->db->rollback();
2315 return -2;
2316 }
2317 } else {
2318 return 0;
2319 }
2320 }
2321
2347 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)
2348 {
2349 global $mysoc, $langs;
2350
2351 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);
2352 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2353
2354 $pu = price2num($pu);
2355 $qty = price2num($qty);
2356 $remise_percent = (float) price2num($remise_percent);
2357 $pu_devise = price2num($pu_devise);
2358
2359 // Check parameters
2360 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2361 if ($type < 0) {
2362 return -1;
2363 }
2364
2365 if ($date_start && $date_end && $date_start > $date_end) {
2366 $langs->load("errors");
2367 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2368 return -1;
2369 }
2370
2371 // Clean parameters
2372 if (empty($vatrate)) {
2373 $vatrate = 0;
2374 }
2375 if (empty($txlocaltax1)) {
2376 $txlocaltax1 = 0;
2377 }
2378 if (empty($txlocaltax2)) {
2379 $txlocaltax2 = 0;
2380 }
2381
2382 $txlocaltax1 = (float) price2num($txlocaltax1);
2383 $txlocaltax2 = (float) price2num($txlocaltax2);
2384
2385 // Calcul du total TTC et de la TVA pour la ligne a partir de
2386 // qty, pu, remise_percent et txtva
2387 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2388 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2389
2390 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2391
2392 $reg = array();
2393
2394 // Clean vat code
2395 $vat_src_code = '';
2396 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2397 $vat_src_code = $reg[1];
2398 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2399 }
2400
2401 $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);
2402 $total_ht = $tabprice[0];
2403 $total_tva = $tabprice[1];
2404 $total_ttc = $tabprice[2];
2405 $pu_ht = $tabprice[3];
2406 $pu_tva = $tabprice[4];
2407 $pu_ttc = $tabprice[5];
2408 $total_localtax1 = $tabprice[9];
2409 $total_localtax2 = $tabprice[10];
2410
2411 // MultiCurrency
2412 $multicurrency_total_ht = $tabprice[16];
2413 $multicurrency_total_tva = $tabprice[17];
2414 $multicurrency_total_ttc = $tabprice[18];
2415 $pu_ht_devise = $tabprice[19];
2416
2417 if (empty($info_bits)) {
2418 $info_bits = 0;
2419 }
2420
2421 //Fetch current line from the database and then clone the object and set it in $oldline property
2422 $line = new SupplierInvoiceLine($this->db);
2423 $line->fetch($id);
2424 $line->fetch_optionals();
2425
2426 $staticline = clone $line;
2427
2428 if ($idproduct) {
2429 $product = new Product($this->db);
2430 $result = $product->fetch($idproduct);
2431 $product_type = $product->type;
2432 } else {
2433 $idproduct = $staticline->fk_product;
2434 $product_type = $type;
2435 }
2436
2437 $line->oldline = $staticline;
2438 $line->context = $this->context;
2439
2440 $line->description = $desc;
2441 $line->desc = $desc;
2442
2443 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2444 $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2445 $line->pu_ht = $line->subprice; // deprecated
2446 $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2447
2448 $line->remise_percent = $remise_percent;
2449 $line->ref_supplier = $ref_supplier;
2450
2451 $line->date_start = $date_start;
2452 $line->date_end = $date_end;
2453
2454 $line->vat_src_code = $vat_src_code;
2455 $line->tva_tx = $vatrate;
2456 $line->localtax1_tx = $txlocaltax1;
2457 $line->localtax2_tx = $txlocaltax2;
2458 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2459 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2460
2461 $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht);
2462 $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva);
2463 $line->total_localtax1 = $total_localtax1;
2464 $line->total_localtax2 = $total_localtax2;
2465 $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc);
2466
2467 $line->fk_product = $idproduct;
2468 $line->product_type = $product_type;
2469 $line->info_bits = $info_bits;
2470 $line->fk_unit = $fk_unit;
2471 $line->rang = $rang;
2472
2473 if (is_array($array_options) && count($array_options) > 0) {
2474 // We replace values in this->line->array_options only for entries defined into $array_options
2475 foreach ($array_options as $key => $value) {
2476 $line->array_options[$key] = $array_options[$key];
2477 }
2478 }
2479
2480 // Multicurrency
2481 $line->multicurrency_subprice = $pu_ht_devise;
2482 $line->multicurrency_total_ht = $multicurrency_total_ht;
2483 $line->multicurrency_total_tva = $multicurrency_total_tva;
2484 $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2485
2486 $res = $line->update($notrigger);
2487
2488 if ($res < 1) {
2489 $this->errors[] = $line->error;
2490 } else {
2491 // Update total price into invoice record
2492 $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2493 }
2494
2495 return $res;
2496 }
2497
2505 public function deleteLine($rowid, $notrigger = 0)
2506 {
2507 if (!$rowid) {
2508 $rowid = $this->id;
2509 }
2510
2511 $this->db->begin();
2512
2513 // Free the discount linked to a line of invoice
2514 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2515 $sql .= ' SET fk_invoice_supplier_line = NULL';
2516 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2517
2518 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2519 $result = $this->db->query($sql);
2520 if (!$result) {
2521 $this->error = $this->db->error();
2522 $this->db->rollback();
2523 return -2;
2524 }
2525
2526 $line = new SupplierInvoiceLine($this->db);
2527
2528 if ($line->fetch($rowid) < 1) {
2529 return -1;
2530 }
2531
2532 $res = $line->delete($notrigger);
2533
2534 if ($res < 1) {
2535 $this->errors[] = $line->error;
2536 $this->db->rollback();
2537 return -3;
2538 } else {
2539 $res = $this->update_price(1);
2540
2541 if ($res > 0) {
2542 $this->db->commit();
2543 return 1;
2544 } else {
2545 $this->db->rollback();
2546 $this->error = $this->db->lasterror();
2547 return -4;
2548 }
2549 }
2550 }
2551
2552
2559 public function info($id)
2560 {
2561 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2562 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2563 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2564 $sql .= ' WHERE c.rowid = '.((int) $id);
2565
2566 $result = $this->db->query($sql);
2567 if ($result) {
2568 if ($this->db->num_rows($result)) {
2569 $obj = $this->db->fetch_object($result);
2570
2571 $this->id = $obj->rowid;
2572
2573 $this->user_creation_id = $obj->fk_user_author;
2574 $this->user_validation_id = $obj->fk_user_valid;
2575 $this->user_modification_id = $obj->fk_user_modif;
2576 $this->date_creation = $this->db->jdate($obj->datec);
2577 $this->date_modification = $this->db->jdate($obj->datem);
2578 //$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)
2579 }
2580 $this->db->free($result);
2581 } else {
2582 dol_print_error($this->db);
2583 }
2584 }
2585
2586 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2595 public function list_replacable_supplier_invoices($socid = 0)
2596 {
2597 // phpcs:enable
2598 global $conf;
2599
2600 $return = array();
2601
2602 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2603 $sql .= " ff.rowid as rowidnext";
2604 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2605 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2606 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2607 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2608 $sql .= " AND f.entity = ".$conf->entity;
2609 $sql .= " AND f.paye = 0"; // Pas classee payee completement
2610 $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2611 $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement
2612 if ($socid > 0) {
2613 $sql .= " AND f.fk_soc = ".((int) $socid);
2614 }
2615 $sql .= " ORDER BY f.ref";
2616
2617 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2618 $resql = $this->db->query($sql);
2619 if ($resql) {
2620 while ($obj = $this->db->fetch_object($resql)) {
2621 $return[$obj->rowid] = array(
2622 'id' => $obj->rowid,
2623 'ref' => $obj->ref,
2624 'status' => $obj->fk_statut
2625 );
2626 }
2627 //print_r($return);
2628 return $return;
2629 } else {
2630 $this->error = $this->db->error();
2631 return -1;
2632 }
2633 }
2634
2635 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2645 public function list_qualified_avoir_supplier_invoices($socid = 0)
2646 {
2647 // phpcs:enable
2648 global $conf;
2649
2650 $return = array();
2651
2652 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn";
2653 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2654 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2655 $sql .= " WHERE f.entity = ".$conf->entity;
2656 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2657 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2658 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2659 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2660 if ($socid > 0) {
2661 $sql .= " AND f.fk_soc = ".((int) $socid);
2662 }
2663 $sql .= " ORDER BY f.ref";
2664
2665 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2666 $resql = $this->db->query($sql);
2667 if ($resql) {
2668 while ($obj = $this->db->fetch_object($resql)) {
2669 $qualified = 0;
2670 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2671 $qualified = 1;
2672 }
2673 if ($obj->fk_statut == self::STATUS_CLOSED) {
2674 $qualified = 1;
2675 }
2676 if ($qualified) {
2677 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2678 $return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot);
2679 }
2680 }
2681
2682 return $return;
2683 } else {
2684 $this->error = $this->db->error();
2685 return -1;
2686 }
2687 }
2688
2689 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2696 public function load_board($user)
2697 {
2698 // phpcs:enable
2699 global $conf, $langs;
2700
2701 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2702 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2703 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
2704 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2705 }
2706 $sql .= ' WHERE ff.paye = 0';
2707 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2708 $sql .= " AND ff.entity = ".$conf->entity;
2709 if ($user->socid) {
2710 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2711 }
2712
2713 $resql = $this->db->query($sql);
2714 if ($resql) {
2715 $langs->load("bills");
2716 $now = dol_now();
2717
2718 $response = new WorkboardResponse();
2719 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2720 $response->label = $langs->trans("SupplierBillsToPay");
2721 $response->labelShort = $langs->trans("StatusToPay");
2722
2723 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2724 $response->img = img_object($langs->trans("Bills"), "bill");
2725
2726 $facturestatic = new FactureFournisseur($this->db);
2727
2728 while ($obj = $this->db->fetch_object($resql)) {
2729 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2730 $facturestatic->statut = $obj->status; // For backward compatibility
2731 $facturestatic->status = $obj->status;
2732
2733 $response->nbtodo++;
2734 $response->total += $obj->total_ht;
2735
2736 if ($facturestatic->hasDelay()) {
2737 $response->nbtodolate++;
2738 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2739 }
2740 }
2741
2742 $this->db->free($resql);
2743 return $response;
2744 } else {
2745 dol_print_error($this->db);
2746 $this->error = $this->db->error();
2747 return -1;
2748 }
2749 }
2750
2758 public function getTooltipContentArray($params)
2759 {
2760 global $conf, $langs, $mysoc;
2761
2762 $langs->load('bills');
2763
2764 $datas = [];
2765 $moretitle = $params['moretitle'] ?? '';
2766
2767 $picto = $this->picto;
2768 if ($this->type == self::TYPE_REPLACEMENT) {
2769 $picto .= 'r'; // Replacement invoice
2770 }
2771 if ($this->type == self::TYPE_CREDIT_NOTE) {
2772 $picto .= 'a'; // Credit note
2773 }
2774 if ($this->type == self::TYPE_DEPOSIT) {
2775 $picto .= 'd'; // Deposit invoice
2776 }
2777
2778 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2779 if ($this->type == self::TYPE_REPLACEMENT) {
2780 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2781 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2782 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2783 } elseif ($this->type == self::TYPE_DEPOSIT) {
2784 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2785 }
2786 if (isset($this->status)) {
2787 $alreadypaid = -1;
2788 if (isset($this->totalpaid)) {
2789 $alreadypaid = $this->totalpaid;
2790 }
2791
2792 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2793 }
2794 if ($moretitle) {
2795 $datas['picto'] .= ' - '.$moretitle;
2796 }
2797 if (!empty($this->ref)) {
2798 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2799 }
2800 if (!empty($this->ref_supplier)) {
2801 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2802 }
2803 if (!empty($this->label)) {
2804 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2805 }
2806 if (!empty($this->date)) {
2807 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2808 }
2809 if (!empty($this->date_echeance)) {
2810 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2811 }
2812 if (!empty($this->total_ht)) {
2813 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2814 }
2815 if (!empty($this->total_tva)) {
2816 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2817 }
2818 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2819 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2820 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2821 }
2822 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2823 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2824 }
2825 if (!empty($this->revenuestamp)) {
2826 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2827 }
2828 if (!empty($this->total_ttc)) {
2829 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2830 }
2831 return $datas;
2832 }
2833
2847 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2848 {
2849 global $langs, $conf, $user, $hookmanager;
2850
2851 $result = '';
2852
2853 if ($option == 'withdraw') {
2854 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2855 } elseif ($option == 'document') {
2856 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2857 } else {
2858 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2859 }
2860
2861 if ($short) {
2862 return $url;
2863 }
2864
2865 if ($option !== 'nolink') {
2866 // Add param to save lastsearch_values or not
2867 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2868 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2869 $add_save_lastsearch_values = 1;
2870 }
2871 if ($add_save_lastsearch_values) {
2872 $url .= '&save_lastsearch_values=1';
2873 }
2874 }
2875
2876 $picto = $this->picto;
2877 if ($this->type == self::TYPE_REPLACEMENT) {
2878 $picto .= 'r'; // Replacement invoice
2879 }
2880 if ($this->type == self::TYPE_CREDIT_NOTE) {
2881 $picto .= 'a'; // Credit note
2882 }
2883 if ($this->type == self::TYPE_DEPOSIT) {
2884 $picto .= 'd'; // Deposit invoice
2885 }
2886
2887 $params = [
2888 'id' => $this->id,
2889 'objecttype' => $this->element,
2890 'option' => $option,
2891 'moretitle' => $moretitle,
2892 ];
2893 $classfortooltip = 'classfortooltip';
2894 $dataparams = '';
2895 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2896 $classfortooltip = 'classforajaxtooltip';
2897 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2898 $label = '';
2899 } else {
2900 $label = implode($this->getTooltipContentArray($params));
2901 }
2902
2903 $ref = $this->ref;
2904 if (empty($ref)) {
2905 $ref = $this->id;
2906 }
2907
2908 $linkclose = '';
2909 if (empty($notooltip)) {
2910 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2911 $label = $langs->trans("ShowSupplierInvoice");
2912 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2913 }
2914 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2915 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2916 }
2917
2918 $linkstart = '<a href="'.$url.'"';
2919 $linkstart .= $linkclose.'>';
2920 $linkend = '</a>';
2921
2922 $result .= $linkstart;
2923 if ($withpicto) {
2924 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2925 }
2926 if ($withpicto != 2) {
2927 $result .= ($max ? dol_trunc($ref, $max) : $ref);
2928 }
2929 $result .= $linkend;
2930
2931 if ($addlinktonotes) {
2932 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2933 if ($txttoshow) {
2934 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2935 $result .= ' <span class="note inline-block">';
2936 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2937 $result .= img_picto('', 'note');
2938 $result .= '</a>';
2939 $result .= '</span>';
2940 }
2941 }
2942 global $action;
2943 $hookmanager->initHooks(array($this->element . 'dao'));
2944 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2945 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2946 if ($reshook > 0) {
2947 $result = $hookmanager->resPrint;
2948 } else {
2949 $result .= $hookmanager->resPrint;
2950 }
2951 return $result;
2952 }
2953
2962 public function getNextNumRef($soc, $mode = 'next')
2963 {
2964 global $db, $langs, $conf;
2965 $langs->load("orders");
2966
2967 // Clean parameters (if not defined or using deprecated value)
2968 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
2969 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2970 }
2971
2972 $mybool = false;
2973
2974 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
2975 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
2976
2977 // Include file with class
2978 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2979
2980 foreach ($dirmodels as $reldir) {
2981 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2982
2983 // Load file with numbering class (if found)
2984 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2985 }
2986
2987 if (!$mybool) {
2988 dol_print_error(null, "Failed to include file ".$file);
2989 return '';
2990 }
2991
2992 $obj = new $classname();
2993 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
2994 $numref = "";
2995 $numref = $obj->getNextValue($soc, $this, $mode);
2996
2997 if ($numref != "") {
2998 return $numref;
2999 } else {
3000 $this->error = $obj->error;
3001 return -1;
3002 }
3003 }
3004
3005
3014 public function initAsSpecimen($option = '')
3015 {
3016 global $langs, $conf;
3017 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3018
3019 $now = dol_now();
3020
3021 // Load array of products prodids
3022 $num_prods = 0;
3023 $prodids = array();
3024
3025 $sql = "SELECT rowid";
3026 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3027 $sql .= " WHERE entity IN (".getEntity('product').")";
3028 $sql .= $this->db->plimit(100);
3029
3030 $resql = $this->db->query($sql);
3031 if ($resql) {
3032 $num_prods = $this->db->num_rows($resql);
3033 $i = 0;
3034 while ($i < $num_prods) {
3035 $i++;
3036 $row = $this->db->fetch_row($resql);
3037 $prodids[$i] = $row[0];
3038 }
3039 }
3040
3041 // Initialise parameters
3042 $this->id = 0;
3043 $this->ref = 'SPECIMEN';
3044 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3045 $this->specimen = 1;
3046 $this->socid = 1;
3047 $this->date = $now;
3048 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3049 $this->cond_reglement_code = 'RECEP';
3050 $this->mode_reglement_code = 'CHQ';
3051
3052 $this->note_public = 'This is a comment (public)';
3053 $this->note_private = 'This is a comment (private)';
3054
3055 $this->multicurrency_tx = 1;
3056 $this->multicurrency_code = $conf->currency;
3057
3058 $xnbp = 0;
3059 if (empty($option) || $option != 'nolines') {
3060 // Lines
3061 $nbp = 5;
3062 while ($xnbp < $nbp) {
3063 $line = new SupplierInvoiceLine($this->db);
3064 $line->desc = $langs->trans("Description")." ".$xnbp;
3065 $line->qty = 1;
3066 $line->subprice = 100;
3067 $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice
3068 $line->price = 100;
3069 $line->tva_tx = 19.6;
3070 $line->localtax1_tx = 0;
3071 $line->localtax2_tx = 0;
3072 if ($xnbp == 2) {
3073 $line->total_ht = 50;
3074 $line->total_ttc = 59.8;
3075 $line->total_tva = 9.8;
3076 $line->remise_percent = 50;
3077 } else {
3078 $line->total_ht = 100;
3079 $line->total_ttc = 119.6;
3080 $line->total_tva = 19.6;
3081 $line->remise_percent = 0;
3082 }
3083
3084 if ($num_prods > 0) {
3085 $prodid = mt_rand(1, $num_prods);
3086 $line->fk_product = $prodids[$prodid];
3087 }
3088 $line->product_type = 0;
3089
3090 $this->lines[$xnbp] = $line;
3091
3092 $this->total_ht += $line->total_ht;
3093 $this->total_tva += $line->total_tva;
3094 $this->total_ttc += $line->total_ttc;
3095
3096 $xnbp++;
3097 }
3098 }
3099
3100 $this->total_ht = $xnbp * 100;
3101 $this->total_tva = $xnbp * 19.6;
3102 $this->total_ttc = $xnbp * 119.6;
3103
3104 return 1;
3105 }
3106
3112 public function loadStateBoard()
3113 {
3114 global $conf, $user;
3115
3116 $this->nb = array();
3117
3118 $clause = "WHERE";
3119
3120 $sql = "SELECT count(f.rowid) as nb";
3121 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3122 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3123 if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3124 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3125 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3126 $clause = "AND";
3127 }
3128 $sql .= " ".$clause." f.entity = ".$conf->entity;
3129
3130 $resql = $this->db->query($sql);
3131 if ($resql) {
3132 while ($obj = $this->db->fetch_object($resql)) {
3133 $this->nb["supplier_invoices"] = $obj->nb;
3134 }
3135 $this->db->free($resql);
3136 return 1;
3137 } else {
3138 dol_print_error($this->db);
3139 $this->error = $this->db->error();
3140 return -1;
3141 }
3142 }
3143
3152 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3153 {
3154 global $conf, $langs;
3155
3156 $error = 0;
3157
3158 $object = new FactureFournisseur($this->db);
3159
3160 $this->db->begin();
3161
3162 // Load source object
3163 $object->fetch($fromid);
3164 $object->id = 0;
3165 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3166 $object->status = self::STATUS_DRAFT;
3167
3168 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3169
3170 // Clear fields
3171 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3172 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3173 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3174 $object->fk_facture_source = 0;
3175 $object->date_creation = '';
3176 $object->date_validation = '';
3177 $object->date = (empty($this->date) ? dol_now() : $this->date);
3178 $object->ref_client = '';
3179 $object->close_code = '';
3180 $object->close_note = '';
3181 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3182 $object->note_private = '';
3183 $object->note_public = '';
3184 }
3185
3186 $object->date_echeance = $object->calculate_date_lim_reglement();
3187
3188 // Loop on each line of new invoice
3189 foreach ($object->lines as $i => $line) {
3190 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3191 unset($object->lines[$i]);
3192 }
3193 }
3194
3195 // Create clone
3196 $object->context['createfromclone'] = 'createfromclone';
3197 $result = $object->create($user);
3198
3199 // Other options
3200 if ($result < 0) {
3201 $this->error = $object->error;
3202 $this->errors = $object->errors;
3203 $error++;
3204 }
3205
3206 if (!$error) {
3207 }
3208
3209 unset($object->context['createfromclone']);
3210
3211 // End
3212 if (!$error) {
3213 $this->db->commit();
3214 return $object->id;
3215 } else {
3216 $this->db->rollback();
3217 return -1;
3218 }
3219 }
3220
3232 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3233 {
3234 global $langs;
3235
3236 $langs->load("suppliers");
3237 $outputlangs->load("products");
3238
3239 // Set the model on the model name to use
3240 if (empty($modele)) {
3241 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3242 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3243 } else {
3244 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3245 }
3246 }
3247
3248 if (empty($modele)) {
3249 return 0;
3250 } else {
3251 $modelpath = "core/modules/supplier_invoice/doc/";
3252
3253 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3254 }
3255 }
3256
3261 public function getRights()
3262 {
3263 global $user;
3264
3265 return $user->hasRight("fournisseur", "facture");
3266 }
3267
3276 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3277 {
3278 $tables = array(
3279 'facture_fourn'
3280 );
3281
3282 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3283 }
3284
3293 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3294 {
3295 $tables = array(
3296 'facture_fourn_det'
3297 );
3298
3299 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3300 }
3301
3307 public function hasDelay()
3308 {
3309 global $conf;
3310
3311 $now = dol_now();
3312
3313 if (!$this->date_echeance) {
3314 return false;
3315 }
3316
3317 $status = isset($this->status) ? $this->status : $this->statut;
3318
3319 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3320 }
3321
3327 public function isCreditNoteUsed()
3328 {
3329 $isUsed = false;
3330
3331 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3332 $resql = $this->db->query($sql);
3333 if (!empty($resql)) {
3334 $obj = $this->db->fetch_object($resql);
3335 if (!empty($obj->fk_invoice_supplier)) {
3336 $isUsed = true;
3337 }
3338 }
3339
3340 return $isUsed;
3341 }
3349 public function getKanbanView($option = '', $arraydata = null)
3350 {
3351 global $langs;
3352
3353 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3354
3355 $picto = $this->picto;
3356 if ($this->type == self::TYPE_REPLACEMENT) {
3357 $picto .= 'r'; // Replacement invoice
3358 }
3359 if ($this->type == self::TYPE_CREDIT_NOTE) {
3360 $picto .= 'a'; // Credit note
3361 }
3362 if ($this->type == self::TYPE_DEPOSIT) {
3363 $picto .= 'd'; // Deposit invoice
3364 }
3365
3366 $return = '<div class="box-flex-item box-flex-grow-zero">';
3367 $return .= '<div class="info-box info-box-sm">';
3368 $return .= '<span class="info-box-icon bg-infobox-action">';
3369 $return .= img_picto('', $picto);
3370 $return .= '</span>';
3371 $return .= '<div class="info-box-content">';
3372 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3373 if ($selected >= 0) {
3374 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3375 }
3376 if (!empty($arraydata['thirdparty'])) {
3377 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3378 }
3379 if (property_exists($this, 'date')) {
3380 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3381 }
3382 if (property_exists($this, 'total_ht')) {
3383 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3384 $return .= ' '.$langs->trans("HT");
3385 $return .= '</span>';
3386 }
3387 if (method_exists($this, 'getLibStatut')) {
3388 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3389 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3390 }
3391 $return .= '</div>';
3392 $return .= '</div>';
3393 $return .= '</div>';
3394 return $return;
3395 }
3396
3403 public function setVATReverseCharge($vatreversecharge)
3404 {
3405 if (!$this->table_element) {
3406 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3407 return -1;
3408 }
3409
3410 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3411
3412 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3413 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3414 $sql .= " WHERE rowid=".((int) $this->id);
3415
3416 if ($this->db->query($sql)) {
3417 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3418 return 1;
3419 } else {
3420 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3421 $this->error = $this->db->error();
3422 return 0;
3423 }
3424 }
3425
3437 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3438 {
3439 global $conf, $langs, $user;
3440
3441 $this->output = '';
3442 $this->error = '';
3443 $nbMailSend = 0;
3444
3445 $error = 0;
3446 $errorsMsg = array();
3447
3448 $langs->load('bills');
3449
3450 if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3451 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3452 return 0;
3453 }
3454 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3455 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3456 return 0;
3457 }
3458
3459 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3460 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3461 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3462 $formmail = new FormMail($this->db);
3463
3464 $now = dol_now();
3465 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3466
3467 $tmpinvoice = new FactureFournisseur($this->db);
3468
3469 dol_syslog(__METHOD__." start", LOG_INFO);
3470
3471 // Select all action comm reminder
3472 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3473 if (!empty($paymentmode) && $paymentmode != 'all') {
3474 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3475 }
3476 $sql .= " WHERE f.paye = 0"; // Only unpaid
3477 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3478 if ($datetouse == 'invoicedate') {
3479 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3480 } else {
3481 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3482 }
3483 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3484 if (!empty($paymentmode) && $paymentmode != 'all') {
3485 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3486 }
3487 // TODO Add a filter to check there is no payment started yet
3488 if ($datetouse == 'invoicedate') {
3489 $sql .= $this->db->order("datef", "ASC");
3490 } else {
3491 $sql .= $this->db->order("date_lim_reglement", "ASC");
3492 }
3493
3494 $resql = $this->db->query($sql);
3495
3496 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3497 if ($datetouse == 'invoicedate') {
3498 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3499 } else {
3500 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3501 }
3502 if (!empty($paymentmode) && $paymentmode != 'all') {
3503 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3504 }
3505 $this->output .= '<br>';
3506
3507 if ($resql) {
3508 while ($obj = $this->db->fetch_object($resql)) {
3509 if (!$error) {
3510 // Load event
3511 $res = $tmpinvoice->fetch($obj->id);
3512 if ($res > 0) {
3513 $tmpinvoice->fetch_thirdparty();
3514
3515 $outputlangs = new Translate('', $conf);
3516 if ($tmpinvoice->thirdparty->default_lang) {
3517 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3518 $outputlangs->loadLangs(array("main", "suppliers"));
3519 } else {
3520 $outputlangs = $langs;
3521 }
3522
3523 // Select email template according to language of recipient
3524 $templateId = 0;
3525 $templateLabel = '';
3526 if (empty($template) || $template == 'EmailTemplateCode') {
3527 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3528 } else {
3529 if (is_numeric($template)) {
3530 $templateId = $template;
3531 } else {
3532 $templateLabel = $template;
3533 }
3534 }
3535
3536 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3537 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3538 $langs->load("errors");
3539 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3540 return 0;
3541 }
3542
3543 // PREPARE EMAIL
3544 $errormesg = '';
3545
3546 // Make substitution in email content
3547 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice);
3548
3549 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3550
3551 // Topic
3552 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3553
3554 // Content
3555 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3556
3557 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3558
3559 // Recipient
3560 $to = array();
3561 if ($forcerecipient) { // If a recipient was forced
3562 $to = array($forcerecipient);
3563 } else {
3564 $res = $tmpinvoice->fetch_thirdparty();
3565 $recipient = $tmpinvoice->thirdparty;
3566 if ($res > 0) {
3567 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3568 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3569 foreach ($tmparraycontact as $data_email) {
3570 if (!empty($data_email['email'])) {
3571 $to[] = $data_email['email'];
3572 }
3573 }
3574 }
3575 if (empty($to) && !empty($recipient->email)) {
3576 $to[] = $recipient->email;
3577 }
3578 if (empty($to)) {
3579 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3580 $error++;
3581 }
3582 } else {
3583 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3584 $error++;
3585 }
3586 }
3587
3588 // Sender
3589 $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3590 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3591 $from = $arraymessage->email_from;
3592 }
3593 if (empty($from)) {
3594 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3595 $error++;
3596 }
3597
3598 if (!$error && !empty($to)) {
3599 $this->db->begin();
3600
3601 $to = implode(',', $to);
3602 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3603 $to = $to.','.$arraymessage->email_to;
3604 }
3605
3606 // Errors Recipient
3607 $errors_to = $conf->global->MAIN_MAIL_ERRORS_TO;
3608
3609 $trackid = 'inv'.$tmpinvoice->id;
3610 $sendcontext = 'standard';
3611
3612 $email_tocc = '';
3613 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3614 $email_tocc = $arraymessage->email_tocc;
3615 }
3616
3617 $email_tobcc = '';
3618 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3619 $email_tobcc = $arraymessage->email_tobcc;
3620 }
3621
3622 // Mail Creation
3623 $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3624
3625 // Sending Mail
3626 if ($cMailFile->sendfile()) {
3627 $nbMailSend++;
3628
3629 // Add a line into event table
3630 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3631
3632 // Insert record of emails sent
3633 $actioncomm = new ActionComm($this->db);
3634
3635 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3636 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3637 $actioncomm->contact_id = 0;
3638
3639 $actioncomm->code = 'AC_EMAIL';
3640 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3641 $actioncomm->note_private = $sendContent;
3642 $actioncomm->fk_project = $tmpinvoice->fk_project;
3643 $actioncomm->datep = dol_now();
3644 $actioncomm->datef = $actioncomm->datep;
3645 $actioncomm->percentage = -1; // Not applicable
3646 $actioncomm->authorid = $user->id; // User saving action
3647 $actioncomm->userownerid = $user->id; // Owner of action
3648 // Fields when action is an email (content should be added into note)
3649 $actioncomm->email_msgid = $cMailFile->msgid;
3650 $actioncomm->email_subject = $sendTopic;
3651 $actioncomm->email_from = $from;
3652 $actioncomm->email_sender = '';
3653 $actioncomm->email_to = $to;
3654 //$actioncomm->email_tocc = $sendtocc;
3655 //$actioncomm->email_tobcc = $sendtobcc;
3656 //$actioncomm->email_subject = $subject;
3657 $actioncomm->errors_to = $errors_to;
3658
3659 $actioncomm->elementtype = 'invoice_supplier';
3660 $actioncomm->fk_element = $tmpinvoice->id;
3661
3662 //$actioncomm->extraparams = $extraparams;
3663
3664 $actioncomm->create($user);
3665 } else {
3666 $errormesg = $cMailFile->error.' : '.$to;
3667 $error++;
3668
3669 // Add a line into event table
3670 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3671
3672 // Insert record of emails sent
3673 $actioncomm = new ActionComm($this->db);
3674
3675 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3676 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3677 $actioncomm->contact_id = 0;
3678
3679 $actioncomm->code = 'AC_EMAIL';
3680 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3681 $actioncomm->note_private = $errormesg;
3682 $actioncomm->fk_project = $tmpinvoice->fk_project;
3683 $actioncomm->datep = dol_now();
3684 $actioncomm->datef = $actioncomm->datep;
3685 $actioncomm->percentage = -1; // Not applicable
3686 $actioncomm->authorid = $user->id; // User saving action
3687 $actioncomm->userownerid = $user->id; // Owner of action
3688 // Fields when action is an email (content should be added into note)
3689 $actioncomm->email_msgid = $cMailFile->msgid;
3690 $actioncomm->email_from = $from;
3691 $actioncomm->email_sender = '';
3692 $actioncomm->email_to = $to;
3693 //$actioncomm->email_tocc = $sendtocc;
3694 //$actioncomm->email_tobcc = $sendtobcc;
3695 //$actioncomm->email_subject = $subject;
3696 $actioncomm->errors_to = $errors_to;
3697
3698 //$actioncomm->extraparams = $extraparams;
3699
3700 $actioncomm->create($user);
3701 }
3702
3703 $this->db->commit(); // We always commit
3704 }
3705
3706 if ($errormesg) {
3707 $errorsMsg[] = $errormesg;
3708 }
3709 } else {
3710 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3711 $error++;
3712 }
3713 }
3714 }
3715 } else {
3716 $error++;
3717 }
3718
3719 if (!$error) {
3720 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3721
3722 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3723
3724 return 0;
3725 } else {
3726 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3727
3728 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3729
3730 return $error;
3731 }
3732 }
3733}
3734
3735
3736
3741{
3745 public $element = 'facture_fourn_det';
3746
3750 public $table_element = 'facture_fourn_det';
3751
3755 public $parent_element = 'facture_fourn';
3756
3760 public $fk_parent_attribute = 'fk_facture_fourn';
3761
3765 public $oldline;
3766
3771 public $ref;
3772
3777 public $product_ref;
3778
3784 public $ref_supplier;
3785
3790 public $product_desc;
3791
3798 public $pu_ht;
3799
3804 public $subprice;
3805
3810 public $pu_ttc;
3811
3812
3817 public $fk_facture_fourn;
3818
3824 public $label;
3825
3831 public $description;
3832
3833 public $date_start;
3834 public $date_end;
3835
3839 public $fk_code_ventilation;
3840
3844 public $skip_update_total; // Skip update price total for special lines
3845
3849 public $situation_percent;
3850
3854 public $fk_prev_id;
3855
3860 public $vat_src_code;
3861
3866 public $tva_tx;
3867
3872 public $localtax1_tx;
3873
3878 public $localtax2_tx;
3879
3884 public $qty;
3885
3890 public $remise_percent;
3891
3896 public $pa_ht;
3897
3902 public $total_ht;
3903
3908 public $total_ttc;
3909
3914 public $total_tva;
3915
3920 public $total_localtax1;
3921
3926 public $total_localtax2;
3927
3931 public $fk_product;
3932
3937 public $product_type;
3938
3943 public $product_label;
3944
3951 public $info_bits;
3952
3957 public $fk_remise_except;
3958
3962 public $fk_parent_line;
3963
3967 public $special_code;
3968
3972 public $rang;
3973
3978 public $localtax1_type;
3979
3984 public $localtax2_type;
3985
3986
3992 public function __construct($db)
3993 {
3994 $this->db = $db;
3995 }
3996
4003 public function fetch($rowid)
4004 {
4005 $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.tva_tx';
4006 $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
4007 $sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_facture_fourn, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
4008 $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
4009 $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
4010 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
4011 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
4012 $sql .= ' WHERE f.rowid = '.((int) $rowid);
4013 $sql .= ' ORDER BY f.rang, f.rowid';
4014
4015 $query = $this->db->query($sql);
4016
4017 if (!$query) {
4018 $this->errors[] = $this->db->error();
4019 return -1;
4020 }
4021
4022 if (!$this->db->num_rows($query)) {
4023 return 0;
4024 }
4025
4026 $obj = $this->db->fetch_object($query);
4027
4028 $this->id = $obj->rowid;
4029 $this->rowid = $obj->rowid;
4030 $this->fk_facture_fourn = $obj->fk_facture_fourn;
4031 $this->description = $obj->line_desc;
4032 $this->desc = $obj->line_desc;
4033 $this->date_start = $obj->date_start;
4034 $this->date_end = $obj->date_end;
4035 $this->product_ref = $obj->product_ref;
4036 $this->ref_supplier = $obj->ref_supplier;
4037 $this->product_desc = $obj->product_desc;
4038
4039 $this->subprice = $obj->pu_ht;
4040 $this->pu_ht = $this->subprice;
4041 $this->pu_ttc = $obj->pu_ttc;
4042 $this->tva_tx = $obj->tva_tx;
4043 $this->localtax1_tx = $obj->localtax1_tx;
4044 $this->localtax2_tx = $obj->localtax2_tx;
4045 $this->localtax1_type = $obj->localtax1_type;
4046 $this->localtax2_type = $obj->localtax2_type;
4047
4048 $this->qty = $obj->qty;
4049 $this->remise_percent = $obj->remise_percent;
4050 $this->fk_remise_except = $obj->fk_remise_except;
4051 //$this->tva = $obj->total_tva; // deprecated
4052 $this->total_ht = $obj->total_ht;
4053 $this->total_tva = $obj->total_tva;
4054 $this->total_localtax1 = $obj->total_localtax1;
4055 $this->total_localtax2 = $obj->total_localtax2;
4056 $this->total_ttc = $obj->total_ttc;
4057 $this->fk_product = $obj->fk_product;
4058 $this->product_type = $obj->product_type;
4059 $this->product_label = $obj->product_label;
4060 $this->label = $obj->product_label;
4061 $this->info_bits = $obj->info_bits;
4062 $this->fk_parent_line = $obj->fk_parent_line;
4063 $this->special_code = $obj->special_code;
4064 $this->rang = $obj->rang;
4065 $this->fk_unit = $obj->fk_unit;
4066
4067 $this->multicurrency_subprice = $obj->multicurrency_subprice;
4068 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
4069 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
4070 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
4071
4072 $this->fetch_optionals();
4073
4074 return 1;
4075 }
4076
4083 public function delete($notrigger = 0)
4084 {
4085 global $user;
4086
4087 dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
4088
4089 $error = 0;
4090
4091 $this->db->begin();
4092
4093 if (!$notrigger) {
4094 if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
4095 $error++;
4096 }
4097 }
4098
4099 $this->deleteObjectLinked();
4100
4101 // Remove extrafields
4102 if (!$error) {
4103 $result = $this->deleteExtraFields();
4104 if ($result < 0) {
4105 $error++;
4106 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4107 }
4108 }
4109
4110 if (!$error) {
4111 // Supprime ligne
4112 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
4113 $sql .= " WHERE rowid = ".((int) $this->id);
4114 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4115 $resql = $this->db->query($sql);
4116 if (!$resql) {
4117 $error++;
4118 $this->error = $this->db->lasterror();
4119 }
4120 }
4121
4122 if (!$error) {
4123 $this->db->commit();
4124 return 1;
4125 } else {
4126 $this->db->rollback();
4127 return -1;
4128 }
4129 }
4130
4137 public function update($notrigger = 0)
4138 {
4139 global $conf;
4140
4141 $pu = price2num($this->subprice);
4142 $qty = price2num($this->qty);
4143
4144 // Check parameters
4145 if (empty($this->qty)) {
4146 $this->qty = 0;
4147 }
4148
4149 if ($this->product_type < 0) {
4150 return -1;
4151 }
4152
4153 // Clean parameters
4154 if (empty($this->remise_percent)) {
4155 $this->remise_percent = 0;
4156 }
4157 if (empty($this->tva_tx)) {
4158 $this->tva_tx = 0;
4159 }
4160 if (empty($this->localtax1_tx)) {
4161 $this->localtax1_tx = 0;
4162 }
4163 if (empty($this->localtax2_tx)) {
4164 $this->localtax2_tx = 0;
4165 }
4166
4167 if (empty($this->pa_ht)) {
4168 $this->pa_ht = 0;
4169 }
4170 if (empty($this->multicurrency_subprice)) {
4171 $this->multicurrency_subprice = 0;
4172 }
4173 if (empty($this->multicurrency_total_ht)) {
4174 $this->multicurrency_total_ht = 0;
4175 }
4176 if (empty($this->multicurrency_total_tva)) {
4177 $this->multicurrency_total_tva = 0;
4178 }
4179 if (empty($this->multicurrency_total_ttc)) {
4180 $this->multicurrency_total_ttc = 0;
4181 }
4182
4183 $fk_product = (int) $this->fk_product;
4184 $fk_unit = (int) $this->fk_unit;
4185
4186 $this->db->begin();
4187
4188 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4189 $sql .= " description = '".$this->db->escape(empty($this->description) ? $this->desc : $this->description)."'";
4190 $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
4191 $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
4192 $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
4193 $sql .= ", pu_ht = ".price2num($this->subprice);
4194 $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
4195 $sql .= ", qty = ".price2num($this->qty);
4196 $sql .= ", remise_percent = ".price2num($this->remise_percent);
4197 if ($this->fk_remise_except > 0) {
4198 $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
4199 } else {
4200 $sql .= ", fk_remise_except=null";
4201 }
4202 $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4203 $sql .= ", tva_tx = ".price2num($this->tva_tx);
4204 $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
4205 $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
4206 $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
4207 $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
4208 $sql .= ", total_ht = ".price2num($this->total_ht);
4209 $sql .= ", tva= ".price2num($this->total_tva);
4210 $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
4211 $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
4212 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4213 $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
4214 $sql .= ", product_type = ".((int) $this->product_type);
4215 $sql .= ", info_bits = ".((int) $this->info_bits);
4216 $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
4217
4218 if (!empty($this->rang)) {
4219 $sql .= ", rang=".((int) $this->rang);
4220 }
4221
4222 // Multicurrency
4223 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4224 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4225 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4226 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4227
4228 $sql .= " WHERE rowid = ".((int) $this->id);
4229
4230 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4231 $resql = $this->db->query($sql);
4232
4233 if (!$resql) {
4234 $this->db->rollback();
4235 $this->error = $this->db->lasterror();
4236 return -1;
4237 }
4238
4239 $this->rowid = $this->id;
4240 $error = 0;
4241
4242 if (!$error) {
4243 $result = $this->insertExtraFields();
4244 if ($result < 0) {
4245 $error++;
4246 }
4247 }
4248
4249 if (!$error && !$notrigger) {
4250 global $langs, $user;
4251
4252 // Call trigger
4253 if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
4254 $this->db->rollback();
4255 return -1;
4256 }
4257 // End call triggers
4258 }
4259
4260 if ($error) {
4261 $this->db->rollback();
4262 return -1;
4263 }
4264
4265 $this->db->commit();
4266 return 1;
4267 }
4268
4276 public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
4277 {
4278 global $user, $langs;
4279
4280 $error = 0;
4281
4282 dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4283
4284 // Clean parameters
4285 $this->desc = trim($this->desc);
4286 if (empty($this->tva_tx)) {
4287 $this->tva_tx = 0;
4288 }
4289 if (empty($this->localtax1_tx)) {
4290 $this->localtax1_tx = 0;
4291 }
4292 if (empty($this->localtax2_tx)) {
4293 $this->localtax2_tx = 0;
4294 }
4295 if (empty($this->localtax1_type)) {
4296 $this->localtax1_type = 0.0;
4297 }
4298 if (empty($this->localtax2_type)) {
4299 $this->localtax2_type = 0.0;
4300 }
4301 if (empty($this->total_tva)) {
4302 $this->total_tva = 0;
4303 }
4304 if (empty($this->total_localtax1)) {
4305 $this->total_localtax1 = 0;
4306 }
4307 if (empty($this->total_localtax2)) {
4308 $this->total_localtax2 = 0;
4309 }
4310 if (empty($this->rang)) {
4311 $this->rang = 0;
4312 }
4313 if (empty($this->remise_percent)) {
4314 $this->remise_percent = 0;
4315 }
4316 if (empty($this->info_bits)) {
4317 $this->info_bits = 0;
4318 }
4319 if (empty($this->subprice)) {
4320 $this->subprice = 0;
4321 }
4322 if (empty($this->special_code)) {
4323 $this->special_code = 0;
4324 }
4325 if (empty($this->fk_parent_line)) {
4326 $this->fk_parent_line = 0;
4327 }
4328 if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
4329 $this->situation_percent = 100;
4330 }
4331
4332 if (empty($this->pa_ht)) {
4333 $this->pa_ht = 0;
4334 }
4335 if (empty($this->multicurrency_subprice)) {
4336 $this->multicurrency_subprice = 0;
4337 }
4338 if (empty($this->multicurrency_total_ht)) {
4339 $this->multicurrency_total_ht = 0;
4340 }
4341 if (empty($this->multicurrency_total_tva)) {
4342 $this->multicurrency_total_tva = 0;
4343 }
4344 if (empty($this->multicurrency_total_ttc)) {
4345 $this->multicurrency_total_ttc = 0;
4346 }
4347
4348
4349 // Check parameters
4350 if ($this->product_type < 0) {
4351 $this->error = 'ErrorProductTypeMustBe0orMore';
4352 return -1;
4353 }
4354 if (!empty($this->fk_product) && $this->fk_product > 0) {
4355 // Check product exists
4356 $result = Product::isExistingObject('product', $this->fk_product);
4357 if ($result <= 0) {
4358 $this->error = 'ErrorProductIdDoesNotExists';
4359 return -1;
4360 }
4361 }
4362
4363 $this->db->begin();
4364
4365 // Insertion dans base de la ligne
4366 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
4367 $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
4368 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4369 $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
4370 $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
4371 $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
4372 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4373 $sql .= ')';
4374 $sql .= " VALUES (".$this->fk_facture_fourn.",";
4375 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4376 $product_label
4377 = !empty($this->product_label)
4378 ? $this->product_label :
4379 (!empty($this->label) ? $this->label : null);
4380 $sql .= " ".(!empty($product_label) ? "'".$this->db->escape($product_label)."'" : "null").",";
4381 $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
4382 $sql .= " '".$this->db->escape($this->ref_supplier)."',";
4383 $sql .= " ".price2num($this->qty).",";
4384
4385 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4386 $sql .= " ".price2num($this->tva_tx).",";
4387 $sql .= " ".price2num($this->localtax1_tx).",";
4388 $sql .= " ".price2num($this->localtax2_tx).",";
4389 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4390 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4391 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4392 $sql .= " ".((int) $this->product_type).",";
4393 $sql .= " ".price2num($this->remise_percent).",";
4394 $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
4395 $sql .= " ".price2num($this->subprice).",";
4396 $sql .= " ".(!empty($this->qty) ? price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
4397 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4398 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4399 $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
4400 $sql .= ' '.((int) $this->rang).',';
4401 $sql .= ' '.((int) $this->special_code).',';
4402 $sql .= " ".((int) $this->info_bits).",";
4403 $sql .= " ".price2num($this->total_ht).",";
4404 $sql .= " ".price2num($this->total_tva).",";
4405 $sql .= " ".price2num($this->total_ttc).",";
4406 $sql .= " ".price2num($this->total_localtax1).",";
4407 $sql .= " ".price2num($this->total_localtax2);
4408 $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4409 $sql .= ", ".(int) $this->fk_multicurrency;
4410 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4411 $sql .= ", ".price2num($this->multicurrency_subprice);
4412 $sql .= ", ".price2num($this->multicurrency_total_ht);
4413 $sql .= ", ".price2num($this->multicurrency_total_tva);
4414 $sql .= ", ".price2num($this->multicurrency_total_ttc);
4415 $sql .= ')';
4416
4417 $resql = $this->db->query($sql);
4418 if ($resql) {
4419 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4420 $this->rowid = $this->id; // backward compatibility
4421
4422 if (!$error) {
4423 $result = $this->insertExtraFields();
4424 if ($result < 0) {
4425 $error++;
4426 }
4427 }
4428
4429 // Si fk_remise_except defini, on lie la remise a la facture
4430 // ce qui la flague comme "consommee".
4431 if ($this->fk_remise_except) {
4432 $discount = new DiscountAbsolute($this->db);
4433 $result = $discount->fetch($this->fk_remise_except);
4434 if ($result >= 0) {
4435 // Check if discount was found
4436 if ($result > 0) {
4437 // Check if discount not already affected to another invoice
4438 if ($discount->fk_facture_line > 0) {
4439 if (empty($noerrorifdiscountalreadylinked)) {
4440 $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4441 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4442 $this->db->rollback();
4443 return -3;
4444 }
4445 } else {
4446 $result = $discount->link_to_invoice($this->id, 0);
4447 if ($result < 0) {
4448 $this->error = $discount->error;
4449 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4450 $this->db->rollback();
4451 return -3;
4452 }
4453 }
4454 } else {
4455 $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4456 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4457 $this->db->rollback();
4458 return -3;
4459 }
4460 } else {
4461 $this->error = $discount->error;
4462 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4463 $this->db->rollback();
4464 return -3;
4465 }
4466 }
4467
4468 if (!$error && !$notrigger) {
4469 // Call trigger
4470 $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4471 if ($result < 0) {
4472 $this->db->rollback();
4473 return -2;
4474 }
4475 // End call triggers
4476 }
4477
4478 $this->db->commit();
4479 return $this->id;
4480 } else {
4481 $this->error = $this->db->error();
4482 $this->db->rollback();
4483 return -2;
4484 }
4485 }
4486
4487 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4493 public function update_total()
4494 {
4495 // phpcs:enable
4496 $this->db->begin();
4497
4498 // Mise a jour ligne en base
4499 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4500 $sql .= " total_ht = ".price2num($this->total_ht);
4501 $sql .= ", tva= ".price2num($this->total_tva);
4502 $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4503 $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4504 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4505 $sql .= " WHERE rowid = ".((int) $this->id);
4506
4507 dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4508
4509 $resql = $this->db->query($sql);
4510 if ($resql) {
4511 $this->db->commit();
4512 return 1;
4513 } else {
4514 $this->error = $this->db->error();
4515 $this->db->rollback();
4516 return -2;
4517 }
4518 }
4519}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:626
$object ref
Definition info.php:79
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Superclass for invoices 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.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
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.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
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 clicable 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 clicable 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 into 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.
fetch($rowid)
Retrieves a supplier invoice line.
update($notrigger=0)
Update a supplier invoice line.
insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
Insert line into database.
update_total()
Mise a jour de l'objet ligne de commande en base.
Class to manage translations.
Class to manage Dolibarr users.
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:654
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:124
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:139
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1929
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:1929