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