dolibarr 20.0.5
fournisseur.facture.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2010-2023 Juanjo Menent <jmenent@simnandez.es>
8 * Copyright (C) 2013-2019 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10 * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2015 Bahfir Abbes <bafbes@gmail.com>
12 * Copyright (C) 2015-2022 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2016-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 $product_type = $_facrec->lines[$i]->product_type;
726 if ($_facrec->lines[$i]->fk_product) {
727 $prod = new Product($this->db);
728 $res = $prod->fetch($_facrec->lines[$i]->fk_product);
729 }
730
731 // For line from template invoice, we use data from template invoice
732 /*
733 $tva_tx = get_default_tva($mysoc,$soc,$prod->id);
734 $tva_npr = get_default_npr($mysoc,$soc,$prod->id);
735 if (empty($tva_tx)) $tva_npr=0;
736 $localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr);
737 $localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr);
738 */
739 $tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : '');
740 $tva_npr = $_facrec->lines[$i]->info_bits;
741 if (empty($tva_tx)) {
742 $tva_npr = 0;
743 }
744 $localtax1_tx = $_facrec->lines[$i]->localtax1_tx;
745 $localtax2_tx = $_facrec->lines[$i]->localtax2_tx;
746
747 $fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price;
748 $buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice;
749
750 // If buyprice not defined from template invoice, we try to guess the best value
751 if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) {
752 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
753 $producttmp = new ProductFournisseur($this->db);
754 $producttmp->fetch($_facrec->lines[$i]->fk_product);
755
756 // If margin module defined on costprice, we try the costprice
757 // If not defined or if module margin defined and pmp and stock module enabled, we try pmp price
758 // else we get the best supplier price
759 if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) {
760 $buyprice = $producttmp->cost_price;
761 } elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) {
762 $buyprice = $producttmp->pmp;
763 } else {
764 if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) {
765 if ($producttmp->product_fourn_price_id > 0) {
766 $buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU');
767 }
768 }
769 }
770 }
771
772 $result_insert = $this->addline(
773 $_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description,
774 $_facrec->lines[$i]->pu_ht,
775 $tva_tx,
776 $localtax1_tx,
777 $localtax2_tx,
778 $_facrec->lines[$i]->qty,
779 $_facrec->lines[$i]->fk_product,
780 $_facrec->lines[$i]->remise_percent,
781 ($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '',
782 ($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '',
783 0,
784 $_facrec->lines[$i]->info_bits,
785 'HT',
786 $product_type,
787 $_facrec->lines[$i]->rang,
788 false,
789 $_facrec->lines[$i]->array_options,
790 $_facrec->lines[$i]->fk_unit,
791 0,
792 0,
793 $_facrec->lines[$i]->ref_supplier,
794 $_facrec->lines[$i]->special_code,
795 0,
796 0
797 );
798 if ($result_insert < 0) {
799 $error++;
800 $this->error = $this->db->error();
801 break;
802 }
803 }
804 }
805
806
807 // Update total price
808 $result = $this->update_price(1);
809 if ($result > 0) {
810 // Actions on extra fields
811 if (!$error) {
812 $result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
813 if ($result < 0) {
814 $error++;
815 }
816 }
817
818 if (!$error) {
819 // Call trigger
820 $result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
821 if ($result < 0) {
822 $error++;
823 }
824 // End call triggers
825 }
826
827 if (!$error) {
828 $this->db->commit();
829 return $this->id;
830 } else {
831 $this->db->rollback();
832 return -4;
833 }
834 } else {
835 $this->error = $langs->trans('FailedToUpdatePrice');
836 $this->db->rollback();
837 return -3;
838 }
839 } else {
840 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
841 $this->error = $langs->trans('ErrorRefAlreadyExists');
842 $this->db->rollback();
843 return -1;
844 } else {
845 $this->error = $this->db->lasterror();
846 $this->db->rollback();
847 return -2;
848 }
849 }
850 }
851
860 public function fetch($id = 0, $ref = '', $ref_ext = '')
861 {
862 if (empty($id) && empty($ref) && empty($ref_ext)) {
863 return -1;
864 }
865
866 $sql = "SELECT";
867 $sql .= " t.rowid,";
868 $sql .= " t.ref,";
869 $sql .= " t.ref_supplier,";
870 $sql .= " t.ref_ext,";
871 $sql .= " t.entity,";
872 $sql .= " t.type,";
873 $sql .= " t.subtype,";
874 $sql .= " t.fk_soc,";
875 $sql .= " t.datec,";
876 $sql .= " t.datef,";
877 $sql .= " t.tms,";
878 $sql .= " t.libelle as label,";
879 $sql .= " t.paye,";
880 $sql .= " t.close_code,";
881 $sql .= " t.close_note,";
882 $sql .= " t.tva,";
883 $sql .= " t.localtax1,";
884 $sql .= " t.localtax2,";
885 $sql .= " t.total_ht,";
886 $sql .= " t.total_tva,";
887 $sql .= " t.total_ttc,";
888 $sql .= " t.fk_statut as status,";
889 $sql .= " t.fk_user_author,";
890 $sql .= " t.fk_user_valid,";
891 $sql .= " t.fk_facture_source,";
892 $sql .= " t.vat_reverse_charge,";
893 $sql .= " t.fk_fac_rec_source,";
894 $sql .= " t.fk_projet as fk_project,";
895 $sql .= " t.fk_cond_reglement,";
896 $sql .= " t.fk_account,";
897 $sql .= " t.fk_mode_reglement,";
898 $sql .= " t.date_lim_reglement,";
899 $sql .= " t.note_private,";
900 $sql .= " t.note_public,";
901 $sql .= " t.model_pdf,";
902 $sql .= " t.last_main_doc,";
903 $sql .= " t.import_key,";
904 $sql .= " t.extraparams,";
905 $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
906 $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
907 $sql .= ' s.nom as socnom, s.rowid as socid,';
908 $sql .= ' t.fk_incoterms, t.location_incoterms,';
909 $sql .= " i.libelle as label_incoterms,";
910 $sql .= ' t.fk_transport_mode,';
911 $sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
912 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
913 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
914 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
915 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
916 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
917 if ($id) {
918 $sql .= " WHERE t.rowid = ".((int) $id);
919 } else {
920 $sql .= ' WHERE t.entity IN ('.getEntity('supplier_invoice').')'; // Don't use entity if you use rowid
921 if ($ref) {
922 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
923 }
924 if ($ref_ext) {
925 $sql .= " AND t.ref_ext = '".$this->db->escape($ref_ext)."'";
926 }
927 }
928
929 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
930 $resql = $this->db->query($sql);
931 if ($resql) {
932 if ($this->db->num_rows($resql)) {
933 $obj = $this->db->fetch_object($resql);
934
935 $this->id = $obj->rowid;
936 $this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
937
938 $this->ref_supplier = $obj->ref_supplier;
939 $this->ref_ext = $obj->ref_ext;
940 $this->entity = $obj->entity;
941 $this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
942 $this->subtype = (int) $obj->subtype;
943 $this->socid = $obj->fk_soc;
944 $this->datec = $this->db->jdate($obj->datec);
945 $this->date = $this->db->jdate($obj->datef);
946 //$this->datep = $this->db->jdate($obj->datef);
947 $this->tms = $this->db->jdate($obj->tms);
948 $this->libelle = $obj->label; // deprecated
949 $this->label = $obj->label;
950 $this->paye = $obj->paye;
951 $this->paid = $obj->paye;
952 $this->close_code = $obj->close_code;
953 $this->close_note = $obj->close_note;
954 $this->total_localtax1 = $obj->localtax1;
955 $this->total_localtax2 = $obj->localtax2;
956 $this->total_ht = $obj->total_ht;
957 $this->total_tva = $obj->total_tva;
958 $this->total_ttc = $obj->total_ttc;
959 $this->status = $obj->status;
960 $this->statut = $obj->status; // For backward compatibility
961 $this->fk_statut = $obj->status; // For backward compatibility
962 $this->user_creation_id = $obj->fk_user_author;
963 $this->author = $obj->fk_user_author; // deprecated
964 $this->user_validation_id = $obj->fk_user_valid;
965 $this->fk_facture_source = $obj->fk_facture_source;
966 $this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1;
967 $this->fk_fac_rec_source = $obj->fk_fac_rec_source;
968 $this->fk_project = $obj->fk_project;
969 $this->cond_reglement_id = $obj->fk_cond_reglement;
970 $this->cond_reglement_code = $obj->cond_reglement_code;
971 $this->cond_reglement = $obj->cond_reglement_label; // deprecated
972 $this->cond_reglement_label = $obj->cond_reglement_label;
973 $this->cond_reglement_doc = $obj->cond_reglement_doc;
974 $this->fk_account = $obj->fk_account;
975 $this->mode_reglement_id = $obj->fk_mode_reglement;
976 $this->mode_reglement_code = $obj->mode_reglement_code;
977 $this->mode_reglement = $obj->mode_reglement_label;
978 $this->date_echeance = $this->db->jdate($obj->date_lim_reglement);
979 $this->note = $obj->note_private; // deprecated
980 $this->note_private = $obj->note_private;
981 $this->note_public = $obj->note_public;
982 $this->model_pdf = $obj->model_pdf;
983 $this->last_main_doc = $obj->last_main_doc;
984 $this->import_key = $obj->import_key;
985
986 //Incoterms
987 $this->fk_incoterms = $obj->fk_incoterms;
988 $this->location_incoterms = $obj->location_incoterms;
989 $this->label_incoterms = $obj->label_incoterms;
990 $this->transport_mode_id = $obj->fk_transport_mode;
991
992 // Multicurrency
993 $this->fk_multicurrency = $obj->fk_multicurrency;
994 $this->multicurrency_code = $obj->multicurrency_code;
995 $this->multicurrency_tx = $obj->multicurrency_tx;
996 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
997 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
998 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
999
1000 $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1001
1002 $this->socid = $obj->socid;
1003
1004 // Retrieve all extrafield
1005 // fetch optionals attributes and labels
1006 $this->fetch_optionals();
1007
1008 $result = $this->fetch_lines();
1009 if ($result < 0) {
1010 $this->error = $this->db->lasterror();
1011 return -3;
1012 }
1013 } else {
1014 $this->error = 'Bill with id '.$id.' not found';
1015 dol_syslog(get_class($this).'::fetch '.$this->error);
1016 return 0;
1017 }
1018
1019 $this->db->free($resql);
1020 return 1;
1021 } else {
1022 $this->error = "Error ".$this->db->lasterror();
1023 return -1;
1024 }
1025 }
1026
1027
1028 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1034 public function fetch_lines()
1035 {
1036 // phpcs:enable
1037 $this->lines = array();
1038
1039 $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';
1040 $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';
1041 $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';
1042 $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';
1043 $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';
1044 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
1045 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
1046 $sql .= ' WHERE fk_facture_fourn='.((int) $this->id);
1047 $sql .= ' ORDER BY f.rang, f.rowid';
1048
1049 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1050
1051 $resql_rows = $this->db->query($sql);
1052 if ($resql_rows) {
1053 $num_rows = $this->db->num_rows($resql_rows);
1054 if ($num_rows) {
1055 $i = 0;
1056 while ($i < $num_rows) {
1057 $obj = $this->db->fetch_object($resql_rows);
1058
1059 $line = new SupplierInvoiceLine($this->db);
1060
1061 $line->id = $obj->rowid;
1062 $line->rowid = $obj->rowid;
1063
1064 $line->description = $obj->line_desc;
1065 $line->desc = $obj->line_desc;
1066 $line->date_start = $this->db->jdate($obj->date_start);
1067 $line->date_end = $this->db->jdate($obj->date_end);
1068
1069 $line->product_ref = $obj->product_ref;
1070 $line->ref = $obj->product_ref;
1071 $line->ref_supplier = $obj->ref_supplier;
1072 $line->libelle = $obj->label;
1073 $line->label = $obj->label;
1074 $line->product_barcode = $obj->product_barcode;
1075 $line->product_desc = $obj->product_desc;
1076 $line->subprice = $obj->pu_ht;
1077 $line->pu_ht = $obj->pu_ht;
1078 $line->pu_ttc = $obj->pu_ttc;
1079 $line->vat_src_code = $obj->vat_src_code;
1080 $line->tva_tx = $obj->tva_tx;
1081 $line->localtax1_tx = $obj->localtax1_tx;
1082 $line->localtax2_tx = $obj->localtax2_tx;
1083 $line->localtax1_type = $obj->localtax1_type;
1084 $line->localtax2_type = $obj->localtax2_type;
1085 $line->qty = $obj->qty;
1086 $line->remise_percent = $obj->remise_percent;
1087 $line->fk_remise_except = $obj->fk_remise_except;
1088 //$line->tva = $obj->total_tva; // deprecated
1089 $line->total_ht = $obj->total_ht;
1090 $line->total_ttc = $obj->total_ttc;
1091 $line->total_tva = $obj->total_tva;
1092 $line->total_localtax1 = $obj->total_localtax1;
1093 $line->total_localtax2 = $obj->total_localtax2;
1094 $line->fk_facture_fourn = $obj->fk_facture_fourn;
1095 $line->fk_product = $obj->fk_product;
1096 $line->product_type = $obj->product_type;
1097 $line->product_label = $obj->label;
1098 $line->info_bits = $obj->info_bits;
1099 $line->fk_parent_line = $obj->fk_parent_line;
1100 $line->special_code = $obj->special_code;
1101 $line->rang = $obj->rang;
1102 $line->fk_unit = $obj->fk_unit;
1103
1104 // Accountancy
1105 $line->fk_accounting_account = $obj->fk_code_ventilation;
1106
1107 // Multicurrency
1108 $line->fk_multicurrency = $obj->fk_multicurrency;
1109 $line->multicurrency_code = $obj->multicurrency_code;
1110 $line->multicurrency_subprice = $obj->multicurrency_subprice;
1111 $line->multicurrency_total_ht = $obj->multicurrency_total_ht;
1112 $line->multicurrency_total_tva = $obj->multicurrency_total_tva;
1113 $line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1114
1115 // Extra fields
1116 $line->fetch_optionals();
1117
1118 $this->lines[$i] = $line;
1119
1120 $i++;
1121 }
1122 }
1123 $this->db->free($resql_rows);
1124 return 1;
1125 } else {
1126 $this->error = $this->db->error();
1127 dol_syslog(get_class($this)."::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG);
1128 return -3;
1129 }
1130 }
1131
1132
1140 public function update($user = null, $notrigger = 0)
1141 {
1142 global $langs;
1143 $error = 0;
1144
1145 // Clean parameters
1146 if (empty($this->type)) {
1147 $this->type = self::TYPE_STANDARD;
1148 }
1149 if (isset($this->ref)) {
1150 $this->ref = trim($this->ref);
1151 }
1152 if (isset($this->ref_supplier)) {
1153 $this->ref_supplier = trim($this->ref_supplier);
1154 }
1155 if (isset($this->ref_ext)) {
1156 $this->ref_ext = trim($this->ref_ext);
1157 }
1158 if (isset($this->entity)) {
1159 $this->entity = (int) $this->entity;
1160 }
1161 if (isset($this->type)) {
1162 $this->type = (int) $this->type;
1163 }
1164 if (isset($this->subtype)) {
1165 $this->subtype = (int) $this->subtype;
1166 }
1167 if (isset($this->socid)) {
1168 $this->socid = (int) $this->socid;
1169 }
1170 if (isset($this->label)) {
1171 $this->label = trim($this->label);
1172 }
1173 if (isset($this->paid)) {
1174 $this->paid = (int) (bool) $this->paye;
1175 $this->paye = $this->paid;
1176 } elseif (isset($this->paye)) {
1177 $this->paid = (int) (bool) $this->paye;
1178 $this->paye = $this->paid;
1179 }
1180 if (isset($this->close_code)) {
1181 $this->close_code = trim($this->close_code);
1182 }
1183 if (isset($this->close_note)) {
1184 $this->close_note = trim($this->close_note);
1185 }
1186 if (empty($this->total_ht)) {
1187 $this->total_ht = 0;
1188 }
1189 if (empty($this->total_tva)) {
1190 $this->total_tva = 0;
1191 }
1192 if (empty($this->total_localtax1)) {
1193 $this->total_localtax1 = 0;
1194 }
1195 if (empty($this->total_localtax2)) {
1196 $this->total_localtax2 = 0;
1197 }
1198 if (isset($this->total_ttc)) {
1199 $this->total_ttc = (float) $this->total_ttc;
1200 }
1201 if (isset($this->status)) {
1202 $this->status = (int) $this->status;
1203 $this->statut = $this->status;
1204 } elseif (isset($this->statut)) {
1205 $this->status = (int) $this->statut;
1206 $this->statut = $this->status;
1207 }
1208 if (isset($this->author)) { // TODO: user_creation_id?
1209 $this->author = (int) $this->author;
1210 }
1211 if (isset($this->fk_user_valid)) {
1212 $this->fk_user_valid = trim($this->fk_user_valid);
1213 }
1214 if (isset($this->fk_facture_source)) {
1215 $this->fk_facture_source = (int) $this->fk_facture_source;
1216 }
1217 if (isset($this->fk_project)) {
1218 if (empty($this->fk_project)) {
1219 $this->fk_project = 0;
1220 } else {
1221 $this->fk_project = (int) $this->fk_project;
1222 }
1223 }
1224 if (isset($this->cond_reglement_id)) {
1225 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1226 }
1227 if (isset($this->note_private)) {
1228 $this->note_private = trim($this->note_private);
1229 $this->note = $this->note_private;
1230 }
1231 if (isset($this->note_public)) {
1232 $this->note_public = trim($this->note_public);
1233 }
1234 if (isset($this->model_pdf)) {
1235 $this->model_pdf = trim($this->model_pdf);
1236 }
1237 if (isset($this->import_key)) {
1238 $this->import_key = trim($this->import_key);
1239 }
1240
1241
1242 // Check parameters
1243 // Put here code to add control on parameters values
1244
1245 // Update request
1246 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1247 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1248 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1249 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1250 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1251 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1252 $sql .= " subtype=".((int) $this->subtype).",";
1253 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1254 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1255 $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1256 if (dol_strlen($this->tms) != 0) {
1257 $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1258 }
1259 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1260 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1261 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1262 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1263 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1264 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1265 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1266 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1267 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1268 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1269 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1270 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1271 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1272 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1273 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1274 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1275 $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1276 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1277 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1278 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1279 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1280 $sql .= " WHERE rowid=".((int) $this->id);
1281
1282 $this->db->begin();
1283
1284 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1285 $resql = $this->db->query($sql);
1286
1287 if (!$resql) {
1288 $error++;
1289
1290 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1291 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1292 } else {
1293 $this->errors[] = "Error ".$this->db->lasterror();
1294 }
1295 }
1296
1297 if (!$error) {
1298 $result = $this->insertExtraFields();
1299 if ($result < 0) {
1300 $error++;
1301 }
1302 }
1303
1304 if (!$error) {
1305 if (!$notrigger) {
1306 // Call trigger
1307 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1308 if ($result < 0) {
1309 $error++;
1310 }
1311 // End call triggers
1312 }
1313 }
1314
1315 // Commit or rollback
1316 if ($error) {
1317 foreach ($this->errors as $errmsg) {
1318 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1319 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1320 }
1321 $this->db->rollback();
1322 return -1 * $error;
1323 } else {
1324 $this->db->commit();
1325 return 1;
1326 }
1327 }
1328
1329 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1336 public function insert_discount($idremise)
1337 {
1338 // phpcs:enable
1339 global $conf, $langs;
1340
1341 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1342 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1343
1344 $this->db->begin();
1345
1346 $remise = new DiscountAbsolute($this->db);
1347 $result = $remise->fetch($idremise);
1348
1349 if ($result > 0) {
1350 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1351 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1352 $this->db->rollback();
1353 return -5;
1354 }
1355
1356 $facligne = new SupplierInvoiceLine($this->db);
1357 $facligne->fk_facture_fourn = $this->id;
1358 $facligne->fk_remise_except = $remise->id;
1359 $facligne->desc = $remise->description; // Description ligne
1360 $facligne->vat_src_code = $remise->vat_src_code;
1361 $facligne->tva_tx = $remise->tva_tx;
1362 $facligne->subprice = -$remise->amount_ht;
1363 $facligne->fk_product = 0; // Id produit predefini
1364 $facligne->product_type = 0;
1365 $facligne->qty = 1;
1366 $facligne->remise_percent = 0;
1367 $facligne->rang = -1;
1368 $facligne->info_bits = 2;
1369
1370 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1371 $facligne->rang = 1;
1372 $linecount = count($this->lines);
1373 for ($ii = 1; $ii <= $linecount; $ii++) {
1374 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1375 }
1376 }
1377
1378 // Get buy/cost price of invoice that is source of discount
1379 if ($remise->fk_invoice_supplier_source > 0) {
1380 $srcinvoice = new FactureFournisseur($this->db);
1381 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1382 $totalcostpriceofinvoice = 0;
1383 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1384 $formmargin = new FormMargin($this->db);
1385 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1386 $facligne->pa_ht = $arraytmp['pa_total'];
1387 }
1388
1389 $facligne->total_ht = -$remise->amount_ht;
1390 $facligne->total_tva = -$remise->amount_tva;
1391 $facligne->total_ttc = -$remise->amount_ttc;
1392
1393 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1394 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1395 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1396 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1397
1398 $lineid = $facligne->insert();
1399 if ($lineid > 0) {
1400 $result = $this->update_price(1);
1401 if ($result > 0) {
1402 // Create link between discount and invoice line
1403 $result = $remise->link_to_invoice($lineid, 0);
1404 if ($result < 0) {
1405 $this->error = $remise->error;
1406 $this->db->rollback();
1407 return -4;
1408 }
1409
1410 $this->db->commit();
1411 return 1;
1412 } else {
1413 $this->error = $facligne->error;
1414 $this->db->rollback();
1415 return -1;
1416 }
1417 } else {
1418 $this->error = $facligne->error;
1419 $this->db->rollback();
1420 return -2;
1421 }
1422 } else {
1423 $this->db->rollback();
1424 return -3;
1425 }
1426 }
1427
1428
1436 public function delete(User $user, $notrigger = 0)
1437 {
1438 global $conf;
1439
1440 $rowid = $this->id;
1441
1442 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1443
1444 // TODO Test if there is at least on payment. If yes, refuse to delete.
1445
1446 $error = 0;
1447 $this->db->begin();
1448
1449 if (!$error && !$notrigger) {
1450 // Call trigger
1451 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1452 if ($result < 0) {
1453 $this->db->rollback();
1454 return -1;
1455 }
1456 // Fin appel triggers
1457 }
1458
1459 if (!$error) {
1460 // If invoice was converted into a discount not yet consumed, we remove discount
1461 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1462 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1463 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1464 $resql = $this->db->query($sql);
1465
1466 // If invoice has consumned discounts
1467 $this->fetch_lines();
1468 $list_rowid_det = array();
1469 foreach ($this->lines as $key => $invoiceline) {
1470 $list_rowid_det[] = $invoiceline->id;
1471 }
1472
1473 // Consumned discounts are freed
1474 if (count($list_rowid_det)) {
1475 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1476 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1477 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1478
1479 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1480 if (!$this->db->query($sql)) {
1481 $error++;
1482 }
1483 }
1484 }
1485
1486 if (!$error) {
1487 $main = MAIN_DB_PREFIX.'facture_fourn_det';
1488 $ef = $main."_extrafields";
1489 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1490 $resqlef = $this->db->query($sqlef);
1491 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1492 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1493 $resql = $this->db->query($sql);
1494 if ($resqlef && $resql) {
1495 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1496 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1497 $resql2 = $this->db->query($sql);
1498 if (!$resql2) {
1499 $error++;
1500 }
1501 } else {
1502 $error++;
1503 }
1504 }
1505
1506 if (!$error) {
1507 // Delete linked object
1508 $res = $this->deleteObjectLinked();
1509 if ($res < 0) {
1510 $error++;
1511 }
1512 }
1513
1514 if (!$error) {
1515 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1516 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1517 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1518
1519 // We remove directory
1520 if ($conf->fournisseur->facture->dir_output) {
1521 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1522
1523 $ref = dol_sanitizeFileName($this->ref);
1524 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1525 $file = $dir."/".$ref.".pdf";
1526 if (file_exists($file)) {
1527 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1528 $this->error = 'ErrorFailToDeleteFile';
1529 $error++;
1530 }
1531 }
1532 if (file_exists($dir)) {
1533 $res = @dol_delete_dir_recursive($dir);
1534
1535 if (!$res) {
1536 $this->error = 'ErrorFailToDeleteDir';
1537 $error++;
1538 }
1539 }
1540 }
1541 }
1542
1543 // Remove extrafields
1544 if (!$error) {
1545 $result = $this->deleteExtraFields();
1546 if ($result < 0) {
1547 $error++;
1548 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1549 }
1550 }
1551
1552 if (!$error) {
1553 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1554 $this->db->commit();
1555 return 1;
1556 } else {
1557 $this->error = $this->db->lasterror();
1558 $this->db->rollback();
1559 return -$error;
1560 }
1561 }
1562
1563
1564 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1575 public function set_paid($user, $close_code = '', $close_note = '')
1576 {
1577 // phpcs:enable
1578 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1579 return $this->setPaid($user, $close_code, $close_note);
1580 }
1581
1590 public function setPaid($user, $close_code = '', $close_note = '')
1591 {
1592 $error = 0;
1593
1594 if ($this->paid != 1) {
1595 $this->db->begin();
1596
1597 $now = dol_now();
1598
1599 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1600
1601 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1602 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1603 if (!$close_code) {
1604 $sql .= ', paye=1';
1605 }
1606 if ($close_code) {
1607 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1608 }
1609 if ($close_note) {
1610 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1611 }
1612 $sql .= ', fk_user_closing = '.((int) $user->id);
1613 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1614 $sql .= ' WHERE rowid = '.((int) $this->id);
1615
1616 $resql = $this->db->query($sql);
1617 if ($resql) {
1618 // Call trigger
1619 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1620 if ($result < 0) {
1621 $error++;
1622 }
1623 // End call triggers
1624 } else {
1625 $error++;
1626 $this->error = $this->db->error();
1627 dol_print_error($this->db);
1628 }
1629
1630 if (!$error) {
1631 $this->db->commit();
1632 return 1;
1633 } else {
1634 $this->db->rollback();
1635 return -1;
1636 }
1637 } else {
1638 return 0;
1639 }
1640 }
1641
1642 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1653 public function set_unpaid($user)
1654 {
1655 // phpcs:enable
1656 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1657 return $this->setUnpaid($user);
1658 }
1659
1668 public function setUnpaid($user)
1669 {
1670 $error = 0;
1671
1672 $this->db->begin();
1673
1674 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1675 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1676 $sql .= ' date_closing=null,';
1677 $sql .= ' fk_user_closing=null';
1678 $sql .= ' WHERE rowid = '.((int) $this->id);
1679
1680 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1681 $resql = $this->db->query($sql);
1682 if ($resql) {
1683 // Call trigger
1684 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1685 if ($result < 0) {
1686 $error++;
1687 }
1688 // End call triggers
1689 } else {
1690 $error++;
1691 $this->error = $this->db->error();
1692 dol_print_error($this->db);
1693 }
1694
1695 if (!$error) {
1696 $this->db->commit();
1697 return 1;
1698 } else {
1699 $this->db->rollback();
1700 return -1;
1701 }
1702 }
1703
1714 public function setCanceled($user, $close_code = '', $close_note = '')
1715 {
1716 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1717
1718 $this->db->begin();
1719
1720 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1721 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1722 if ($close_code) {
1723 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1724 }
1725 if ($close_note) {
1726 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1727 }
1728 $sql .= " WHERE rowid = ".((int) $this->id);
1729
1730 $resql = $this->db->query($sql);
1731 if ($resql) {
1732 // Bound discounts are deducted from the invoice
1733 // as they have not been used since the invoice is abandoned.
1734 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1735 $sql .= ' SET fk_invoice_supplier = NULL';
1736 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1737
1738 $resql = $this->db->query($sql);
1739 if ($resql) {
1740 // Call trigger
1741 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1742 if ($result < 0) {
1743 $this->db->rollback();
1744 return -1;
1745 }
1746 // End call triggers
1747
1748 $this->db->commit();
1749 return 1;
1750 } else {
1751 $this->error = $this->db->error()." sql=".$sql;
1752 $this->db->rollback();
1753 return -1;
1754 }
1755 } else {
1756 $this->error = $this->db->error()." sql=".$sql;
1757 $this->db->rollback();
1758 return -2;
1759 }
1760 }
1761
1771 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1772 {
1773 global $mysoc, $conf, $langs;
1774
1775 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1776
1777 $now = dol_now();
1778
1779 $error = 0;
1780 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1781
1782 // Force to have object complete for checks
1783 $this->fetch_thirdparty();
1784 $this->fetch_lines();
1785
1786 // Check parameters
1787 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1788 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1789 return 0;
1790 }
1791 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1792 $langs->load("errors");
1793 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1794 return -1;
1795 }
1796 if (count($this->lines) <= 0) {
1797 $langs->load("errors");
1798 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1799 return -1;
1800 }
1801
1802 // Check for mandatory fields in thirdparty (defined into setup)
1803 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1804 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1805 foreach ($array_to_check as $key) {
1806 $keymin = strtolower($key);
1807 if ($keymin == 'accountancy_code_supplier') {
1808 $keymin = 'code_compta_fournisseur';
1809 }
1810 if (!property_exists($this->thirdparty, $keymin)) {
1811 continue;
1812 }
1813 $vallabel = $this->thirdparty->$keymin;
1814
1815 $i = (int) preg_replace('/[^0-9]/', '', $key);
1816 if ($i > 0) {
1817 if ($this->thirdparty->isACompany()) {
1818 // Check for mandatory prof id (but only if country is other than ours)
1819 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1820 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1821 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1822 $langs->load("errors");
1823 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1824 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1825 return -1;
1826 }
1827 }
1828 }
1829 } else {
1830 if ($key == 'EMAIL') {
1831 // Check for mandatory
1832 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1833 $langs->load("errors");
1834 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1835 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1836 return -1;
1837 }
1838 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1839 // Check for mandatory
1840 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1841 $langs->load("errors");
1842 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1843 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1844 return -1;
1845 }
1846 }
1847 }
1848 }
1849 }
1850
1851 $this->db->begin();
1852
1853 // Define new ref
1854 if ($force_number) {
1855 $num = $force_number;
1856 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1857 $num = $this->getNextNumRef($this->thirdparty);
1858 } else {
1859 $num = $this->ref;
1860 }
1861 $this->newref = dol_sanitizeFileName($num);
1862
1863 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1864 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1865 $sql .= " WHERE rowid = ".((int) $this->id);
1866
1867 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1868 $resql = $this->db->query($sql);
1869 if ($resql) {
1870 // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1871 if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1872 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1873 $langs->load("agenda");
1874
1875 $cpt = count($this->lines);
1876 for ($i = 0; $i < $cpt; $i++) {
1877 if ($this->lines[$i]->fk_product > 0) {
1878 $mouvP = new MouvementStock($this->db);
1879 $mouvP->origin = &$this;
1880 $mouvP->setOrigin($this->element, $this->id);
1881 // We increase stock for product
1882 $up_ht_disc = $this->lines[$i]->subprice;
1883 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1884 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1885 }
1887 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1888 } else {
1889 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1890 }
1891 if ($result < 0) {
1892 $this->error = $mouvP->error;
1893 if (count($mouvP->errors)) {
1894 $this->errors = $mouvP->errors;
1895 }
1896 return -2;
1897 }
1898 }
1899 }
1900 }
1901
1902 // Triggers call
1903 if (!$error && empty($notrigger)) {
1904 // Call trigger
1905 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1906 if ($result < 0) {
1907 $error++;
1908 }
1909 // End call triggers
1910 }
1911
1912 if (!$error) {
1913 $this->oldref = $this->ref;
1914
1915 // Rename directory if dir was a temporary ref
1916 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1917 // Now we rename also files into index
1918 $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)."'";
1919 $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;
1920 $resql = $this->db->query($sql);
1921 if (!$resql) {
1922 $error++;
1923 $this->error = $this->db->lasterror();
1924 }
1925 $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)."'";
1926 $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1927 $resql = $this->db->query($sql);
1928 if (!$resql) {
1929 $error++;
1930 $this->error = $this->db->lasterror();
1931 }
1932
1933 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1934 $oldref = dol_sanitizeFileName($this->ref);
1935 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1936 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1937 if (!$error && file_exists($dirsource)) {
1938 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1939
1940 if (@rename($dirsource, $dirdest)) {
1941 dol_syslog("Rename ok");
1942 // Rename docs starting with $oldref with $this->newref
1943 $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, '/'));
1944 foreach ($listoffiles as $fileentry) {
1945 $dirsource = $fileentry['name'];
1946 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1947 $dirsource = $fileentry['path'].'/'.$dirsource;
1948 $dirdest = $fileentry['path'].'/'.$dirdest;
1949 @rename($dirsource, $dirdest);
1950 }
1951 }
1952 }
1953 }
1954 }
1955
1956 // Set new ref and define current statut
1957 if (!$error) {
1958 $this->ref = $this->newref;
1961 //$this->date_validation=$now; this is stored into log table
1962 }
1963
1964 if (!$error) {
1965 $this->db->commit();
1966 return 1;
1967 } else {
1968 $this->db->rollback();
1969 return -1;
1970 }
1971 } else {
1972 $this->error = $this->db->error();
1973 $this->db->rollback();
1974 return -1;
1975 }
1976 }
1977
1986 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
1987 {
1988 // phpcs:enable
1989 global $conf, $langs;
1990
1991 $error = 0;
1992
1993 if ($this->status == self::STATUS_DRAFT) {
1994 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1995 return 0;
1996 }
1997
1998 dol_syslog(__METHOD__, LOG_DEBUG);
1999
2000 $this->db->begin();
2001
2002 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2003 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2004 $sql .= " WHERE rowid = ".((int) $this->id);
2005
2006 $result = $this->db->query($sql);
2007 if ($result) {
2008 if (!$error) {
2009 $this->oldcopy = clone $this;
2010 }
2011
2012 // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
2013 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2014 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2015 $langs->load("agenda");
2016
2017 $cpt = count($this->lines);
2018 for ($i = 0; $i < $cpt; $i++) {
2019 if ($this->lines[$i]->fk_product > 0) {
2020 $mouvP = new MouvementStock($this->db);
2021 $mouvP->origin = &$this;
2022 $mouvP->setOrigin($this->element, $this->id);
2023 // We increase stock for product
2025 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2026 } else {
2027 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2028 }
2029 }
2030 }
2031 }
2032 // Triggers call
2033 if (!$error && empty($notrigger)) {
2034 // Call trigger
2035 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2036 if ($result < 0) {
2037 $error++;
2038 }
2039 // End call triggers
2040 }
2041 if ($error == 0) {
2042 $this->db->commit();
2043 return 1;
2044 } else {
2045 $this->db->rollback();
2046 return -1;
2047 }
2048 } else {
2049 $this->error = $this->db->error();
2050 $this->db->rollback();
2051 return -1;
2052 }
2053 }
2054
2055
2089 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)
2090 {
2091 global $langs, $mysoc;
2092
2093 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);
2094 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2095
2096 if ($this->status == self::STATUS_DRAFT) {
2097 // Clean parameters
2098 if (empty($remise_percent)) {
2099 $remise_percent = 0;
2100 }
2101 if (empty($qty)) {
2102 $qty = 0;
2103 }
2104 if (empty($info_bits)) {
2105 $info_bits = 0;
2106 }
2107 if (empty($rang)) {
2108 $rang = 0;
2109 }
2110 if (empty($fk_code_ventilation)) {
2111 $fk_code_ventilation = 0;
2112 }
2113 if (empty($txtva)) {
2114 $txtva = 0;
2115 }
2116 if (empty($txlocaltax1)) {
2117 $txlocaltax1 = 0;
2118 }
2119 if (empty($txlocaltax2)) {
2120 $txlocaltax2 = 0;
2121 }
2122
2123 $remise_percent = price2num($remise_percent);
2124 $qty = price2num($qty);
2125 $pu = price2num($pu);
2126 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2127 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2128 }
2129 $txlocaltax1 = price2num($txlocaltax1);
2130 $txlocaltax2 = price2num($txlocaltax2);
2131
2132 if ($date_start && $date_end && $date_start > $date_end) {
2133 $langs->load("errors");
2134 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2135 return -1;
2136 }
2137
2138 $this->db->begin();
2139
2140 if ($fk_product > 0) {
2141 if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) {
2142 // Check quantity is enough
2143 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2144 $prod = new ProductFournisseur($this->db);
2145 if ($prod->fetch($fk_product) > 0) {
2146 $product_type = $prod->type;
2147 $label = $prod->label;
2148 $fk_prod_fourn_price = 0;
2149
2150 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2151 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2152 $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
2153 if ($result > 0) {
2154 if (empty($pu)) {
2155 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2156 }
2157 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2158 // is remise percent not keyed but present for the product we add it
2159 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2160 $remise_percent = $prod->remise_percent;
2161 }
2162 }
2163 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2164 $langs->load("errors");
2165 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2166 $this->db->rollback();
2167 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2168 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2169 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2170 return -1;
2171 }
2172 if ($result == -1) {
2173 $langs->load("errors");
2174 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2175 $this->db->rollback();
2176 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2177 return -1;
2178 }
2179 if ($result < -1) {
2180 $this->error = $prod->error;
2181 $this->db->rollback();
2182 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2183 return -1;
2184 }
2185 } else {
2186 $this->error = $prod->error;
2187 $this->db->rollback();
2188 return -1;
2189 }
2190 }
2191 } else {
2192 $product_type = $type;
2193 }
2194
2195 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2196 $pu = 0;
2197 }
2198
2199 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2200
2201 // Clean vat code
2202 $reg = array();
2203 $vat_src_code = '';
2204 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2205 $vat_src_code = $reg[1];
2206 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2207 }
2208
2209 // Calcul du total TTC et de la TVA pour la ligne a partir de
2210 // qty, pu, remise_percent et txtva
2211 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2212 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2213
2214 $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);
2215 $total_ht = $tabprice[0];
2216 $total_tva = $tabprice[1];
2217 $total_ttc = $tabprice[2];
2218 $total_localtax1 = $tabprice[9];
2219 $total_localtax2 = $tabprice[10];
2220 $pu_ht = $tabprice[3];
2221
2222 // MultiCurrency
2223 $multicurrency_total_ht = $tabprice[16];
2224 $multicurrency_total_tva = $tabprice[17];
2225 $multicurrency_total_ttc = $tabprice[18];
2226 $pu_ht_devise = $tabprice[19];
2227
2228 // Check parameters
2229 if ($type < 0) {
2230 return -1;
2231 }
2232
2233 if ($rang < 0) {
2234 $rangmax = $this->line_max();
2235 $rang = $rangmax + 1;
2236 }
2237
2238 // Insert line
2239 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2240
2241 $supplierinvoiceline->context = $this->context;
2242
2243 $supplierinvoiceline->fk_facture_fourn = $this->id;
2244 //$supplierinvoiceline->label=$label; // deprecated
2245 $supplierinvoiceline->desc = $desc;
2246 $supplierinvoiceline->ref_supplier = $ref_supplier;
2247
2248 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2249 $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2250
2251 $supplierinvoiceline->vat_src_code = $vat_src_code;
2252 $supplierinvoiceline->tva_tx = $txtva;
2253 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2254 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2255 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2256 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2257
2258 $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
2259 $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
2260 $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
2261 $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
2262 $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
2263
2264 $supplierinvoiceline->fk_product = $fk_product;
2265 $supplierinvoiceline->product_type = $type;
2266 $supplierinvoiceline->remise_percent = $remise_percent;
2267 $supplierinvoiceline->date_start = $date_start;
2268 $supplierinvoiceline->date_end = $date_end;
2269 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2270 $supplierinvoiceline->rang = $rang;
2271 $supplierinvoiceline->info_bits = $info_bits;
2272 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2273
2274
2275 $supplierinvoiceline->special_code = (int) $special_code;
2276 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2277 $supplierinvoiceline->origin = $this->origin;
2278 $supplierinvoiceline->origin_id = $origin_id;
2279 $supplierinvoiceline->fk_unit = $fk_unit;
2280
2281 // Multicurrency
2282 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2283 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2284 $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
2285
2286 $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
2287 $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
2288 $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
2289
2290 if (is_array($array_options) && count($array_options) > 0) {
2291 $supplierinvoiceline->array_options = $array_options;
2292 }
2293
2294 $result = $supplierinvoiceline->insert($notrigger);
2295 if ($result > 0) {
2296 // Reorder if child line
2297 if (!empty($fk_parent_line)) {
2298 $this->line_order(true, 'DESC');
2299 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2300 $linecount = count($this->lines);
2301 for ($ii = $rang; $ii <= $linecount; $ii++) {
2302 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2303 }
2304 }
2305
2306 // Mise a jour information denormalisees au niveau de la facture meme
2307 $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.
2308 if ($result > 0) {
2309 $this->db->commit();
2310 return $supplierinvoiceline->id;
2311 } else {
2312 $this->error = $this->db->error();
2313 $this->db->rollback();
2314 return -1;
2315 }
2316 } else {
2317 $this->error = $supplierinvoiceline->error;
2318 $this->errors = $supplierinvoiceline->errors;
2319 $this->db->rollback();
2320 return -2;
2321 }
2322 } else {
2323 return 0;
2324 }
2325 }
2326
2352 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)
2353 {
2354 global $mysoc, $langs;
2355
2356 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);
2357 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2358
2359 $pu = price2num($pu);
2360 $qty = price2num($qty);
2361 $remise_percent = (float) price2num($remise_percent);
2362 $pu_devise = price2num($pu_devise);
2363
2364 // Check parameters
2365 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2366 if ($type < 0) {
2367 return -1;
2368 }
2369
2370 if ($date_start && $date_end && $date_start > $date_end) {
2371 $langs->load("errors");
2372 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2373 return -1;
2374 }
2375
2376 // Clean parameters
2377 if (empty($vatrate)) {
2378 $vatrate = 0;
2379 }
2380 if (empty($txlocaltax1)) {
2381 $txlocaltax1 = 0;
2382 }
2383 if (empty($txlocaltax2)) {
2384 $txlocaltax2 = 0;
2385 }
2386
2387 $txlocaltax1 = (float) price2num($txlocaltax1);
2388 $txlocaltax2 = (float) price2num($txlocaltax2);
2389
2390 // Calcul du total TTC et de la TVA pour la ligne a partir de
2391 // qty, pu, remise_percent et txtva
2392 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2393 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2394
2395 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2396
2397 $reg = array();
2398
2399 // Clean vat code
2400 $vat_src_code = '';
2401 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2402 $vat_src_code = $reg[1];
2403 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2404 }
2405
2406 $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);
2407 $total_ht = $tabprice[0];
2408 $total_tva = $tabprice[1];
2409 $total_ttc = $tabprice[2];
2410 $pu_ht = $tabprice[3];
2411 $pu_tva = $tabprice[4];
2412 $pu_ttc = $tabprice[5];
2413 $total_localtax1 = $tabprice[9];
2414 $total_localtax2 = $tabprice[10];
2415
2416 // MultiCurrency
2417 $multicurrency_total_ht = $tabprice[16];
2418 $multicurrency_total_tva = $tabprice[17];
2419 $multicurrency_total_ttc = $tabprice[18];
2420 $pu_ht_devise = $tabprice[19];
2421
2422 if (empty($info_bits)) {
2423 $info_bits = 0;
2424 }
2425
2426 //Fetch current line from the database and then clone the object and set it in $oldline property
2427 $line = new SupplierInvoiceLine($this->db);
2428 $line->fetch($id);
2429 $line->fetch_optionals();
2430
2431 $staticline = clone $line;
2432
2433 if ($idproduct) {
2434 $product = new Product($this->db);
2435 $result = $product->fetch($idproduct);
2436 $product_type = $product->type;
2437 } else {
2438 $idproduct = $staticline->fk_product;
2439 $product_type = $type;
2440 }
2441
2442 $line->oldline = $staticline;
2443 $line->context = $this->context;
2444
2445 $line->description = $desc;
2446 $line->desc = $desc;
2447
2448 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2449 $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2450 $line->pu_ht = $line->subprice; // deprecated
2451 $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2452
2453 $line->remise_percent = $remise_percent;
2454 $line->ref_supplier = $ref_supplier;
2455
2456 $line->date_start = $date_start;
2457 $line->date_end = $date_end;
2458
2459 $line->vat_src_code = $vat_src_code;
2460 $line->tva_tx = $vatrate;
2461 $line->localtax1_tx = $txlocaltax1;
2462 $line->localtax2_tx = $txlocaltax2;
2463 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2464 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2465
2466 $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht);
2467 $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva);
2468 $line->total_localtax1 = $total_localtax1;
2469 $line->total_localtax2 = $total_localtax2;
2470 $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc);
2471
2472 $line->fk_product = $idproduct;
2473 $line->product_type = $product_type;
2474 $line->info_bits = $info_bits;
2475 $line->fk_unit = $fk_unit;
2476 $line->rang = $rang;
2477
2478 if (is_array($array_options) && count($array_options) > 0) {
2479 // We replace values in this->line->array_options only for entries defined into $array_options
2480 foreach ($array_options as $key => $value) {
2481 $line->array_options[$key] = $array_options[$key];
2482 }
2483 }
2484
2485 // Multicurrency
2486 $line->multicurrency_subprice = $pu_ht_devise;
2487 $line->multicurrency_total_ht = $multicurrency_total_ht;
2488 $line->multicurrency_total_tva = $multicurrency_total_tva;
2489 $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2490
2491 $res = $line->update($notrigger);
2492
2493 if ($res < 1) {
2494 $this->errors[] = $line->error;
2495 $this->errors = array_merge($this->errors, $line->errors);
2496 } else {
2497 // Update total price into invoice record
2498 $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2499 }
2500
2501 return $res;
2502 }
2503
2511 public function deleteLine($rowid, $notrigger = 0)
2512 {
2513 if (!$rowid) {
2514 $rowid = $this->id;
2515 }
2516
2517 $this->db->begin();
2518
2519 // Free the discount linked to a line of invoice
2520 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2521 $sql .= ' SET fk_invoice_supplier_line = NULL';
2522 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2523
2524 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2525 $result = $this->db->query($sql);
2526 if (!$result) {
2527 $this->error = $this->db->error();
2528 $this->db->rollback();
2529 return -2;
2530 }
2531
2532 $line = new SupplierInvoiceLine($this->db);
2533
2534 if ($line->fetch($rowid) < 1) {
2535 return -1;
2536 }
2537
2538 $res = $line->delete($notrigger);
2539
2540 if ($res < 1) {
2541 $this->errors[] = $line->error;
2542 $this->db->rollback();
2543 return -3;
2544 } else {
2545 $res = $this->update_price(1);
2546
2547 if ($res > 0) {
2548 $this->db->commit();
2549 return 1;
2550 } else {
2551 $this->db->rollback();
2552 $this->error = $this->db->lasterror();
2553 return -4;
2554 }
2555 }
2556 }
2557
2558
2565 public function info($id)
2566 {
2567 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2568 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2569 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2570 $sql .= ' WHERE c.rowid = '.((int) $id);
2571
2572 $result = $this->db->query($sql);
2573 if ($result) {
2574 if ($this->db->num_rows($result)) {
2575 $obj = $this->db->fetch_object($result);
2576
2577 $this->id = $obj->rowid;
2578
2579 $this->user_creation_id = $obj->fk_user_author;
2580 $this->user_validation_id = $obj->fk_user_valid;
2581 $this->user_modification_id = $obj->fk_user_modif;
2582 $this->date_creation = $this->db->jdate($obj->datec);
2583 $this->date_modification = $this->db->jdate($obj->datem);
2584 //$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)
2585 }
2586 $this->db->free($result);
2587 } else {
2588 dol_print_error($this->db);
2589 }
2590 }
2591
2592 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2601 public function list_replacable_supplier_invoices($socid = 0)
2602 {
2603 // phpcs:enable
2604 global $conf;
2605
2606 $return = array();
2607
2608 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2609 $sql .= " ff.rowid as rowidnext";
2610 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2611 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2612 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2613 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2614 $sql .= " AND f.entity = ".$conf->entity;
2615 $sql .= " AND f.paye = 0"; // Pas classee payee completement
2616 $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2617 $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement
2618 if ($socid > 0) {
2619 $sql .= " AND f.fk_soc = ".((int) $socid);
2620 }
2621 $sql .= " ORDER BY f.ref";
2622
2623 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2624 $resql = $this->db->query($sql);
2625 if ($resql) {
2626 while ($obj = $this->db->fetch_object($resql)) {
2627 $return[$obj->rowid] = array(
2628 'id' => $obj->rowid,
2629 'ref' => $obj->ref,
2630 'status' => $obj->fk_statut
2631 );
2632 }
2633 //print_r($return);
2634 return $return;
2635 } else {
2636 $this->error = $this->db->error();
2637 return -1;
2638 }
2639 }
2640
2641 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2651 public function list_qualified_avoir_supplier_invoices($socid = 0)
2652 {
2653 // phpcs:enable
2654 global $conf;
2655
2656 $return = array();
2657
2658 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn";
2659 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2660 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2661 $sql .= " WHERE f.entity = ".$conf->entity;
2662 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2663 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2664 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2665 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2666 if ($socid > 0) {
2667 $sql .= " AND f.fk_soc = ".((int) $socid);
2668 }
2669 $sql .= " ORDER BY f.ref";
2670
2671 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2672 $resql = $this->db->query($sql);
2673 if ($resql) {
2674 while ($obj = $this->db->fetch_object($resql)) {
2675 $qualified = 0;
2676 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2677 $qualified = 1;
2678 }
2679 if ($obj->fk_statut == self::STATUS_CLOSED) {
2680 $qualified = 1;
2681 }
2682 if ($qualified) {
2683 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2684 $return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot);
2685 }
2686 }
2687
2688 return $return;
2689 } else {
2690 $this->error = $this->db->error();
2691 return -1;
2692 }
2693 }
2694
2695 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2702 public function load_board($user)
2703 {
2704 // phpcs:enable
2705 global $conf, $langs;
2706
2707 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2708 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2709 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2710 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2711 }
2712 $sql .= ' WHERE ff.paye = 0';
2713 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2714 $sql .= " AND ff.entity = ".$conf->entity;
2715 if ($user->socid) {
2716 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2717 }
2718
2719 $resql = $this->db->query($sql);
2720 if ($resql) {
2721 $langs->load("bills");
2722 $now = dol_now();
2723
2724 $response = new WorkboardResponse();
2725 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2726 $response->label = $langs->trans("SupplierBillsToPay");
2727 $response->labelShort = $langs->trans("StatusToPay");
2728
2729 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2730 $response->img = img_object($langs->trans("Bills"), "bill");
2731
2732 $facturestatic = new FactureFournisseur($this->db);
2733
2734 while ($obj = $this->db->fetch_object($resql)) {
2735 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2736 $facturestatic->statut = $obj->status; // For backward compatibility
2737 $facturestatic->status = $obj->status;
2738
2739 $response->nbtodo++;
2740 $response->total += $obj->total_ht;
2741
2742 if ($facturestatic->hasDelay()) {
2743 $response->nbtodolate++;
2744 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2745 }
2746 }
2747
2748 $this->db->free($resql);
2749 return $response;
2750 } else {
2751 dol_print_error($this->db);
2752 $this->error = $this->db->error();
2753 return -1;
2754 }
2755 }
2756
2764 public function getTooltipContentArray($params)
2765 {
2766 global $conf, $langs, $mysoc;
2767
2768 $langs->load('bills');
2769
2770 $datas = [];
2771 $moretitle = $params['moretitle'] ?? '';
2772
2773 $picto = $this->picto;
2774 if ($this->type == self::TYPE_REPLACEMENT) {
2775 $picto .= 'r'; // Replacement invoice
2776 }
2777 if ($this->type == self::TYPE_CREDIT_NOTE) {
2778 $picto .= 'a'; // Credit note
2779 }
2780 if ($this->type == self::TYPE_DEPOSIT) {
2781 $picto .= 'd'; // Deposit invoice
2782 }
2783
2784 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2785 if ($this->type == self::TYPE_REPLACEMENT) {
2786 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2787 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2788 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2789 } elseif ($this->type == self::TYPE_DEPOSIT) {
2790 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2791 }
2792 if (isset($this->status)) {
2793 $alreadypaid = -1;
2794 if (isset($this->alreadypaid)) {
2795 $alreadypaid = $this->alreadypaid;
2796 }
2797
2798 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2799 }
2800 if ($moretitle) {
2801 $datas['picto'] .= ' - '.$moretitle;
2802 }
2803 if (!empty($this->ref)) {
2804 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2805 }
2806 if (!empty($this->ref_supplier)) {
2807 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2808 }
2809 if (!empty($this->label)) {
2810 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2811 }
2812 if (!empty($this->date)) {
2813 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2814 }
2815 if (!empty($this->date_echeance)) {
2816 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2817 }
2818 if (!empty($this->total_ht)) {
2819 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2820 }
2821 if (!empty($this->total_tva)) {
2822 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2823 }
2824 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2825 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2826 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2827 }
2828 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2829 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2830 }
2831 if (!empty($this->revenuestamp)) {
2832 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2833 }
2834 if (!empty($this->total_ttc)) {
2835 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2836 }
2837 return $datas;
2838 }
2839
2853 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2854 {
2855 global $langs, $conf, $user, $hookmanager;
2856
2857 $result = '';
2858
2859 if ($option == 'withdraw') {
2860 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2861 } elseif ($option == 'document') {
2862 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2863 } else {
2864 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2865 }
2866
2867 if ($short) {
2868 return $url;
2869 }
2870
2871 if ($option !== 'nolink') {
2872 // Add param to save lastsearch_values or not
2873 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2874 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2875 $add_save_lastsearch_values = 1;
2876 }
2877 if ($add_save_lastsearch_values) {
2878 $url .= '&save_lastsearch_values=1';
2879 }
2880 }
2881
2882 $picto = $this->picto;
2883 if ($this->type == self::TYPE_REPLACEMENT) {
2884 $picto .= 'r'; // Replacement invoice
2885 }
2886 if ($this->type == self::TYPE_CREDIT_NOTE) {
2887 $picto .= 'a'; // Credit note
2888 }
2889 if ($this->type == self::TYPE_DEPOSIT) {
2890 $picto .= 'd'; // Deposit invoice
2891 }
2892
2893 $params = [
2894 'id' => $this->id,
2895 'objecttype' => $this->element,
2896 'option' => $option,
2897 'moretitle' => $moretitle,
2898 ];
2899 $classfortooltip = 'classfortooltip';
2900 $dataparams = '';
2901 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2902 $classfortooltip = 'classforajaxtooltip';
2903 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2904 $label = '';
2905 } else {
2906 $label = implode($this->getTooltipContentArray($params));
2907 }
2908
2909 $ref = $this->ref;
2910 if (empty($ref)) {
2911 $ref = $this->id;
2912 }
2913
2914 $linkclose = '';
2915 if (empty($notooltip)) {
2916 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2917 $label = $langs->trans("ShowSupplierInvoice");
2918 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2919 }
2920 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2921 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2922 }
2923
2924 $linkstart = '<a href="'.$url.'"';
2925 $linkstart .= $linkclose.'>';
2926 $linkend = '</a>';
2927
2928 $result .= $linkstart;
2929 if ($withpicto) {
2930 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2931 }
2932 if ($withpicto != 2) {
2933 $result .= ($max ? dol_trunc($ref, $max) : $ref);
2934 }
2935 $result .= $linkend;
2936
2937 if ($addlinktonotes) {
2938 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2939 if ($txttoshow) {
2940 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2941 $result .= ' <span class="note inline-block">';
2942 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2943 $result .= img_picto('', 'note');
2944 $result .= '</a>';
2945 $result .= '</span>';
2946 }
2947 }
2948 global $action;
2949 $hookmanager->initHooks(array($this->element . 'dao'));
2950 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2951 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2952 if ($reshook > 0) {
2953 $result = $hookmanager->resPrint;
2954 } else {
2955 $result .= $hookmanager->resPrint;
2956 }
2957 return $result;
2958 }
2959
2968 public function getNextNumRef($soc, $mode = 'next')
2969 {
2970 global $db, $langs, $conf;
2971 $langs->load("orders");
2972
2973 // Clean parameters (if not defined or using deprecated value)
2974 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
2975 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2976 }
2977
2978 $mybool = false;
2979
2980 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
2981 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
2982
2983 // Include file with class
2984 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2985
2986 foreach ($dirmodels as $reldir) {
2987 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2988
2989 // Load file with numbering class (if found)
2990 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2991 }
2992
2993 if (!$mybool) {
2994 dol_print_error(null, "Failed to include file ".$file);
2995 return '';
2996 }
2997
2998 $obj = new $classname();
2999 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3000 $numref = "";
3001 $numref = $obj->getNextValue($soc, $this, $mode);
3002
3003 if ($numref != "") {
3004 return $numref;
3005 } else {
3006 $this->error = $obj->error;
3007 return -1;
3008 }
3009 }
3010
3011
3020 public function initAsSpecimen($option = '')
3021 {
3022 global $langs, $conf;
3023 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3024
3025 $now = dol_now();
3026
3027 // Load array of products prodids
3028 $num_prods = 0;
3029 $prodids = array();
3030
3031 $sql = "SELECT rowid";
3032 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3033 $sql .= " WHERE entity IN (".getEntity('product').")";
3034 $sql .= $this->db->plimit(100);
3035
3036 $resql = $this->db->query($sql);
3037 if ($resql) {
3038 $num_prods = $this->db->num_rows($resql);
3039 $i = 0;
3040 while ($i < $num_prods) {
3041 $i++;
3042 $row = $this->db->fetch_row($resql);
3043 $prodids[$i] = $row[0];
3044 }
3045 }
3046
3047 // Initialise parameters
3048 $this->id = 0;
3049 $this->ref = 'SPECIMEN';
3050 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3051 $this->specimen = 1;
3052 $this->socid = 1;
3053 $this->date = $now;
3054 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3055 $this->cond_reglement_code = 'RECEP';
3056 $this->mode_reglement_code = 'CHQ';
3057
3058 $this->note_public = 'This is a comment (public)';
3059 $this->note_private = 'This is a comment (private)';
3060
3061 $this->multicurrency_tx = 1;
3062 $this->multicurrency_code = $conf->currency;
3063
3064 $xnbp = 0;
3065 if (empty($option) || $option != 'nolines') {
3066 // Lines
3067 $nbp = 5;
3068 while ($xnbp < $nbp) {
3069 $line = new SupplierInvoiceLine($this->db);
3070 $line->desc = $langs->trans("Description")." ".$xnbp;
3071 $line->qty = 1;
3072 $line->subprice = 100;
3073 $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice
3074 $line->price = 100;
3075 $line->tva_tx = 19.6;
3076 $line->localtax1_tx = 0;
3077 $line->localtax2_tx = 0;
3078 if ($xnbp == 2) {
3079 $line->total_ht = 50;
3080 $line->total_ttc = 59.8;
3081 $line->total_tva = 9.8;
3082 $line->remise_percent = 50;
3083 } else {
3084 $line->total_ht = 100;
3085 $line->total_ttc = 119.6;
3086 $line->total_tva = 19.6;
3087 $line->remise_percent = 0;
3088 }
3089
3090 if ($num_prods > 0) {
3091 $prodid = mt_rand(1, $num_prods);
3092 $line->fk_product = $prodids[$prodid];
3093 }
3094 $line->product_type = 0;
3095
3096 $this->lines[$xnbp] = $line;
3097
3098 $this->total_ht += $line->total_ht;
3099 $this->total_tva += $line->total_tva;
3100 $this->total_ttc += $line->total_ttc;
3101
3102 $xnbp++;
3103 }
3104 }
3105
3106 $this->total_ht = $xnbp * 100;
3107 $this->total_tva = $xnbp * 19.6;
3108 $this->total_ttc = $xnbp * 119.6;
3109
3110 return 1;
3111 }
3112
3118 public function loadStateBoard()
3119 {
3120 global $conf, $user;
3121
3122 $this->nb = array();
3123
3124 $clause = "WHERE";
3125
3126 $sql = "SELECT count(f.rowid) as nb";
3127 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3128 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3129 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3130 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3131 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3132 $clause = "AND";
3133 }
3134 $sql .= " ".$clause." f.entity = ".$conf->entity;
3135
3136 $resql = $this->db->query($sql);
3137 if ($resql) {
3138 while ($obj = $this->db->fetch_object($resql)) {
3139 $this->nb["supplier_invoices"] = $obj->nb;
3140 }
3141 $this->db->free($resql);
3142 return 1;
3143 } else {
3144 dol_print_error($this->db);
3145 $this->error = $this->db->error();
3146 return -1;
3147 }
3148 }
3149
3158 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3159 {
3160 global $conf, $langs;
3161
3162 $error = 0;
3163
3164 $object = new FactureFournisseur($this->db);
3165
3166 $this->db->begin();
3167
3168 // Load source object
3169 $object->fetch($fromid);
3170 $object->id = 0;
3171 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3172 $object->status = self::STATUS_DRAFT;
3173
3174 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3175
3176 // Clear fields
3177 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3178 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3179 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3180 $object->fk_facture_source = 0;
3181 $object->date_creation = '';
3182 $object->date_validation = '';
3183 $object->date = (empty($this->date) ? dol_now() : $this->date);
3184 $object->ref_client = '';
3185 $object->close_code = '';
3186 $object->close_note = '';
3187 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3188 $object->note_private = '';
3189 $object->note_public = '';
3190 }
3191
3192 $object->date_echeance = $object->calculate_date_lim_reglement();
3193
3194 // Loop on each line of new invoice
3195 foreach ($object->lines as $i => $line) {
3196 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3197 unset($object->lines[$i]);
3198 }
3199 }
3200
3201 // Create clone
3202 $object->context['createfromclone'] = 'createfromclone';
3203 $result = $object->create($user);
3204
3205 // Other options
3206 if ($result < 0) {
3207 $this->error = $object->error;
3208 $this->errors = $object->errors;
3209 $error++;
3210 }
3211
3212 if (!$error) {
3213 }
3214
3215 unset($object->context['createfromclone']);
3216
3217 // End
3218 if (!$error) {
3219 $this->db->commit();
3220 return $object->id;
3221 } else {
3222 $this->db->rollback();
3223 return -1;
3224 }
3225 }
3226
3238 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3239 {
3240 global $langs;
3241
3242 $langs->load("suppliers");
3243 $outputlangs->load("products");
3244
3245 // Set the model on the model name to use
3246 if (empty($modele)) {
3247 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3248 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3249 } else {
3250 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3251 }
3252 }
3253
3254 if (empty($modele)) {
3255 return 0;
3256 } else {
3257 $modelpath = "core/modules/supplier_invoice/doc/";
3258
3259 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3260 }
3261 }
3262
3267 public function getRights()
3268 {
3269 global $user;
3270
3271 return $user->hasRight("fournisseur", "facture");
3272 }
3273
3282 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3283 {
3284 $tables = array(
3285 'facture_fourn'
3286 );
3287
3288 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3289 }
3290
3299 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3300 {
3301 $tables = array(
3302 'facture_fourn_det'
3303 );
3304
3305 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3306 }
3307
3313 public function hasDelay()
3314 {
3315 global $conf;
3316
3317 $now = dol_now();
3318
3319 if (!$this->date_echeance) {
3320 return false;
3321 }
3322
3323 $status = isset($this->status) ? $this->status : $this->statut;
3324
3325 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3326 }
3327
3333 public function isCreditNoteUsed()
3334 {
3335 $isUsed = false;
3336
3337 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3338 $resql = $this->db->query($sql);
3339 if (!empty($resql)) {
3340 $obj = $this->db->fetch_object($resql);
3341 if (!empty($obj->fk_invoice_supplier)) {
3342 $isUsed = true;
3343 }
3344 }
3345
3346 return $isUsed;
3347 }
3355 public function getKanbanView($option = '', $arraydata = null)
3356 {
3357 global $langs;
3358
3359 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3360
3361 $picto = $this->picto;
3362 if ($this->type == self::TYPE_REPLACEMENT) {
3363 $picto .= 'r'; // Replacement invoice
3364 }
3365 if ($this->type == self::TYPE_CREDIT_NOTE) {
3366 $picto .= 'a'; // Credit note
3367 }
3368 if ($this->type == self::TYPE_DEPOSIT) {
3369 $picto .= 'd'; // Deposit invoice
3370 }
3371
3372 $return = '<div class="box-flex-item box-flex-grow-zero">';
3373 $return .= '<div class="info-box info-box-sm">';
3374 $return .= '<span class="info-box-icon bg-infobox-action">';
3375 $return .= img_picto('', $picto);
3376 $return .= '</span>';
3377 $return .= '<div class="info-box-content">';
3378 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3379 if ($selected >= 0) {
3380 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3381 }
3382 if (!empty($arraydata['thirdparty'])) {
3383 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3384 }
3385 if (property_exists($this, 'date')) {
3386 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3387 }
3388 if (property_exists($this, 'total_ht')) {
3389 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3390 $return .= ' '.$langs->trans("HT");
3391 $return .= '</span>';
3392 }
3393 if (method_exists($this, 'getLibStatut')) {
3394 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3395 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3396 }
3397 $return .= '</div>';
3398 $return .= '</div>';
3399 $return .= '</div>';
3400 return $return;
3401 }
3402
3409 public function setVATReverseCharge($vatreversecharge)
3410 {
3411 if (!$this->table_element) {
3412 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3413 return -1;
3414 }
3415
3416 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3417
3418 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3419 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3420 $sql .= " WHERE rowid=".((int) $this->id);
3421
3422 if ($this->db->query($sql)) {
3423 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3424 return 1;
3425 } else {
3426 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3427 $this->error = $this->db->error();
3428 return 0;
3429 }
3430 }
3431
3443 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3444 {
3445 global $conf, $langs, $user;
3446
3447 $this->output = '';
3448 $this->error = '';
3449 $nbMailSend = 0;
3450
3451 $error = 0;
3452 $errorsMsg = array();
3453
3454 $langs->load('bills');
3455
3456 if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3457 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3458 return 0;
3459 }
3460 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3461 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3462 return 0;
3463 }
3464
3465 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3466 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3467 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3468 $formmail = new FormMail($this->db);
3469
3470 $now = dol_now();
3471 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3472
3473 $tmpinvoice = new FactureFournisseur($this->db);
3474
3475 dol_syslog(__METHOD__." start", LOG_INFO);
3476
3477 // Select all action comm reminder
3478 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3479 if (!empty($paymentmode) && $paymentmode != 'all') {
3480 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3481 }
3482 $sql .= " WHERE f.paye = 0"; // Only unpaid
3483 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3484 if ($datetouse == 'invoicedate') {
3485 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3486 } else {
3487 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3488 }
3489 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3490 if (!empty($paymentmode) && $paymentmode != 'all') {
3491 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3492 }
3493 // TODO Add a filter to check there is no payment started yet
3494 if ($datetouse == 'invoicedate') {
3495 $sql .= $this->db->order("datef", "ASC");
3496 } else {
3497 $sql .= $this->db->order("date_lim_reglement", "ASC");
3498 }
3499
3500 $resql = $this->db->query($sql);
3501
3502 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3503 if ($datetouse == 'invoicedate') {
3504 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3505 } else {
3506 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3507 }
3508 if (!empty($paymentmode) && $paymentmode != 'all') {
3509 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3510 }
3511 $this->output .= '<br>';
3512
3513 if ($resql) {
3514 while ($obj = $this->db->fetch_object($resql)) {
3515 if (!$error) {
3516 // Load event
3517 $res = $tmpinvoice->fetch($obj->id);
3518 if ($res > 0) {
3519 $tmpinvoice->fetch_thirdparty();
3520
3521 $outputlangs = new Translate('', $conf);
3522 if ($tmpinvoice->thirdparty->default_lang) {
3523 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3524 $outputlangs->loadLangs(array("main", "suppliers"));
3525 } else {
3526 $outputlangs = $langs;
3527 }
3528
3529 // Select email template according to language of recipient
3530 $templateId = 0;
3531 $templateLabel = '';
3532 if (empty($template) || $template == 'EmailTemplateCode') {
3533 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3534 } else {
3535 if (is_numeric($template)) {
3536 $templateId = $template;
3537 } else {
3538 $templateLabel = $template;
3539 }
3540 }
3541
3542 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3543 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3544 $langs->load("errors");
3545 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3546 return 0;
3547 }
3548
3549 // PREPARE EMAIL
3550 $errormesg = '';
3551
3552 // Make substitution in email content
3553 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice);
3554
3555 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3556
3557 // Topic
3558 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3559
3560 // Content
3561 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3562
3563 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3564
3565 // Recipient
3566 $to = array();
3567 if ($forcerecipient) { // If a recipient was forced
3568 $to = array($forcerecipient);
3569 } else {
3570 $res = $tmpinvoice->fetch_thirdparty();
3571 $recipient = $tmpinvoice->thirdparty;
3572 if ($res > 0) {
3573 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3574 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3575 foreach ($tmparraycontact as $data_email) {
3576 if (!empty($data_email['email'])) {
3577 $to[] = $data_email['email'];
3578 }
3579 }
3580 }
3581 if (empty($to) && !empty($recipient->email)) {
3582 $to[] = $recipient->email;
3583 }
3584 if (empty($to)) {
3585 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3586 $error++;
3587 }
3588 } else {
3589 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3590 $error++;
3591 }
3592 }
3593
3594 // Sender
3595 $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3596 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3597 $from = $arraymessage->email_from;
3598 }
3599 if (empty($from)) {
3600 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3601 $error++;
3602 }
3603
3604 if (!$error && !empty($to)) {
3605 $this->db->begin();
3606
3607 $to = implode(',', $to);
3608 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3609 $to = $to.','.$arraymessage->email_to;
3610 }
3611
3612 // Errors Recipient
3613 $errors_to = $conf->global->MAIN_MAIL_ERRORS_TO;
3614
3615 $trackid = 'inv'.$tmpinvoice->id;
3616 $sendcontext = 'standard';
3617
3618 $email_tocc = '';
3619 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3620 $email_tocc = $arraymessage->email_tocc;
3621 }
3622
3623 $email_tobcc = '';
3624 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3625 $email_tobcc = $arraymessage->email_tobcc;
3626 }
3627
3628 // Mail Creation
3629 $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3630
3631 // Sending Mail
3632 if ($cMailFile->sendfile()) {
3633 $nbMailSend++;
3634
3635 // Add a line into event table
3636 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3637
3638 // Insert record of emails sent
3639 $actioncomm = new ActionComm($this->db);
3640
3641 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3642 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3643 $actioncomm->contact_id = 0;
3644
3645 $actioncomm->code = 'AC_EMAIL';
3646 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3647 $actioncomm->note_private = $sendContent;
3648 $actioncomm->fk_project = $tmpinvoice->fk_project;
3649 $actioncomm->datep = dol_now();
3650 $actioncomm->datef = $actioncomm->datep;
3651 $actioncomm->percentage = -1; // Not applicable
3652 $actioncomm->authorid = $user->id; // User saving action
3653 $actioncomm->userownerid = $user->id; // Owner of action
3654 // Fields when action is an email (content should be added into note)
3655 $actioncomm->email_msgid = $cMailFile->msgid;
3656 $actioncomm->email_subject = $sendTopic;
3657 $actioncomm->email_from = $from;
3658 $actioncomm->email_sender = '';
3659 $actioncomm->email_to = $to;
3660 //$actioncomm->email_tocc = $sendtocc;
3661 //$actioncomm->email_tobcc = $sendtobcc;
3662 //$actioncomm->email_subject = $subject;
3663 $actioncomm->errors_to = $errors_to;
3664
3665 $actioncomm->elementtype = 'invoice_supplier';
3666 $actioncomm->fk_element = $tmpinvoice->id;
3667
3668 //$actioncomm->extraparams = $extraparams;
3669
3670 $actioncomm->create($user);
3671 } else {
3672 $errormesg = $cMailFile->error.' : '.$to;
3673 $error++;
3674
3675 // Add a line into event table
3676 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3677
3678 // Insert record of emails sent
3679 $actioncomm = new ActionComm($this->db);
3680
3681 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3682 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3683 $actioncomm->contact_id = 0;
3684
3685 $actioncomm->code = 'AC_EMAIL';
3686 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3687 $actioncomm->note_private = $errormesg;
3688 $actioncomm->fk_project = $tmpinvoice->fk_project;
3689 $actioncomm->datep = dol_now();
3690 $actioncomm->datef = $actioncomm->datep;
3691 $actioncomm->percentage = -1; // Not applicable
3692 $actioncomm->authorid = $user->id; // User saving action
3693 $actioncomm->userownerid = $user->id; // Owner of action
3694 // Fields when action is an email (content should be added into note)
3695 $actioncomm->email_msgid = $cMailFile->msgid;
3696 $actioncomm->email_from = $from;
3697 $actioncomm->email_sender = '';
3698 $actioncomm->email_to = $to;
3699 //$actioncomm->email_tocc = $sendtocc;
3700 //$actioncomm->email_tobcc = $sendtobcc;
3701 //$actioncomm->email_subject = $subject;
3702 $actioncomm->errors_to = $errors_to;
3703
3704 //$actioncomm->extraparams = $extraparams;
3705
3706 $actioncomm->create($user);
3707 }
3708
3709 $this->db->commit(); // We always commit
3710 }
3711
3712 if ($errormesg) {
3713 $errorsMsg[] = $errormesg;
3714 }
3715 } else {
3716 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3717 $error++;
3718 }
3719 }
3720 }
3721 } else {
3722 $error++;
3723 }
3724
3725 if (!$error) {
3726 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3727
3728 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3729
3730 return 0;
3731 } else {
3732 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3733
3734 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3735
3736 return $error;
3737 }
3738 }
3739}
3740
3741
3742
3747{
3751 public $element = 'facture_fourn_det';
3752
3756 public $table_element = 'facture_fourn_det';
3757
3761 public $parent_element = 'facture_fourn';
3762
3766 public $fk_parent_attribute = 'fk_facture_fourn';
3767
3771 public $oldline;
3772
3777 public $ref;
3778
3783 public $product_ref;
3784
3790 public $ref_supplier;
3791
3796 public $product_desc;
3797
3804 public $pu_ht;
3805
3810 public $subprice;
3811
3816 public $pu_ttc;
3817
3818
3823 public $fk_facture_fourn;
3824
3830 public $label;
3831
3837 public $description;
3838
3839 public $date_start;
3840 public $date_end;
3841
3845 public $fk_code_ventilation;
3846
3850 public $skip_update_total; // Skip update price total for special lines
3851
3855 public $situation_percent;
3856
3860 public $fk_prev_id;
3861
3866 public $vat_src_code;
3867
3872 public $tva_tx;
3873
3878 public $localtax1_tx;
3879
3884 public $localtax2_tx;
3885
3890 public $qty;
3891
3896 public $remise_percent;
3897
3902 public $pa_ht;
3903
3908 public $total_ht;
3909
3914 public $total_ttc;
3915
3920 public $total_tva;
3921
3926 public $total_localtax1;
3927
3932 public $total_localtax2;
3933
3937 public $fk_product;
3938
3943 public $product_type;
3944
3949 public $product_label;
3950
3957 public $info_bits;
3958
3963 public $fk_remise_except;
3964
3968 public $fk_parent_line;
3969
3973 public $special_code;
3974
3978 public $rang;
3979
3984 public $localtax1_type;
3985
3990 public $localtax2_type;
3991
3992
3998 public function __construct($db)
3999 {
4000 $this->db = $db;
4001 }
4002
4009 public function fetch($rowid)
4010 {
4011 $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';
4012 $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
4013 $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';
4014 $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
4015 $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
4016 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
4017 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
4018 $sql .= ' WHERE f.rowid = '.((int) $rowid);
4019 $sql .= ' ORDER BY f.rang, f.rowid';
4020
4021 $query = $this->db->query($sql);
4022
4023 if (!$query) {
4024 $this->errors[] = $this->db->error();
4025 return -1;
4026 }
4027
4028 if (!$this->db->num_rows($query)) {
4029 return 0;
4030 }
4031
4032 $obj = $this->db->fetch_object($query);
4033
4034 $this->id = $obj->rowid;
4035 $this->rowid = $obj->rowid;
4036 $this->fk_facture_fourn = $obj->fk_facture_fourn;
4037
4038 $this->description = $obj->line_desc;
4039 $this->desc = $obj->line_desc;
4040 $this->date_start = $this->db->jdate($obj->date_start);
4041 $this->date_end = $this->db->jdate($obj->date_end);
4042 $this->product_ref = $obj->product_ref;
4043 $this->ref_supplier = $obj->ref_supplier;
4044 $this->product_desc = $obj->product_desc;
4045
4046 $this->subprice = $obj->pu_ht;
4047 $this->pu_ht = $this->subprice;
4048 $this->pu_ttc = $obj->pu_ttc;
4049 $this->tva_tx = $obj->tva_tx;
4050 $this->localtax1_tx = $obj->localtax1_tx;
4051 $this->localtax2_tx = $obj->localtax2_tx;
4052 $this->localtax1_type = $obj->localtax1_type;
4053 $this->localtax2_type = $obj->localtax2_type;
4054
4055 $this->qty = $obj->qty;
4056 $this->remise_percent = $obj->remise_percent;
4057 $this->fk_remise_except = $obj->fk_remise_except;
4058 //$this->tva = $obj->total_tva; // deprecated
4059 $this->total_ht = $obj->total_ht;
4060 $this->total_tva = $obj->total_tva;
4061 $this->total_localtax1 = $obj->total_localtax1;
4062 $this->total_localtax2 = $obj->total_localtax2;
4063 $this->total_ttc = $obj->total_ttc;
4064 $this->fk_product = $obj->fk_product;
4065 $this->product_type = $obj->product_type;
4066 $this->product_label = $obj->product_label;
4067 $this->label = $obj->product_label;
4068 $this->info_bits = $obj->info_bits;
4069 $this->fk_parent_line = $obj->fk_parent_line;
4070 $this->special_code = $obj->special_code;
4071 $this->rang = $obj->rang;
4072 $this->fk_unit = $obj->fk_unit;
4073
4074 $this->multicurrency_subprice = $obj->multicurrency_subprice;
4075 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
4076 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
4077 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
4078
4079 $this->fetch_optionals();
4080
4081 return 1;
4082 }
4083
4090 public function delete($notrigger = 0)
4091 {
4092 global $user;
4093
4094 dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
4095
4096 $error = 0;
4097
4098 $this->db->begin();
4099
4100 if (!$notrigger) {
4101 if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
4102 $error++;
4103 }
4104 }
4105
4106 $this->deleteObjectLinked();
4107
4108 // Remove extrafields
4109 if (!$error) {
4110 $result = $this->deleteExtraFields();
4111 if ($result < 0) {
4112 $error++;
4113 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4114 }
4115 }
4116
4117 if (!$error) {
4118 // Supprime ligne
4119 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
4120 $sql .= " WHERE rowid = ".((int) $this->id);
4121 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4122 $resql = $this->db->query($sql);
4123 if (!$resql) {
4124 $error++;
4125 $this->error = $this->db->lasterror();
4126 }
4127 }
4128
4129 if (!$error) {
4130 $this->db->commit();
4131 return 1;
4132 } else {
4133 $this->db->rollback();
4134 return -1;
4135 }
4136 }
4137
4144 public function update($notrigger = 0)
4145 {
4146 global $conf;
4147
4148 $pu = price2num($this->subprice);
4149 $qty = price2num($this->qty);
4150
4151 // Check parameters
4152 if (empty($this->qty)) {
4153 $this->qty = 0;
4154 }
4155
4156 if ($this->product_type < 0) {
4157 return -1;
4158 }
4159
4160 // Clean parameters
4161 if (empty($this->remise_percent)) {
4162 $this->remise_percent = 0;
4163 }
4164 if (empty($this->tva_tx)) {
4165 $this->tva_tx = 0;
4166 }
4167 if (empty($this->localtax1_tx)) {
4168 $this->localtax1_tx = 0;
4169 }
4170 if (empty($this->localtax2_tx)) {
4171 $this->localtax2_tx = 0;
4172 }
4173
4174 if (empty($this->pa_ht)) {
4175 $this->pa_ht = 0;
4176 }
4177 if (empty($this->multicurrency_subprice)) {
4178 $this->multicurrency_subprice = 0;
4179 }
4180 if (empty($this->multicurrency_total_ht)) {
4181 $this->multicurrency_total_ht = 0;
4182 }
4183 if (empty($this->multicurrency_total_tva)) {
4184 $this->multicurrency_total_tva = 0;
4185 }
4186 if (empty($this->multicurrency_total_ttc)) {
4187 $this->multicurrency_total_ttc = 0;
4188 }
4189
4190 $fk_product = (int) $this->fk_product;
4191 $fk_unit = (int) $this->fk_unit;
4192
4193 $this->db->begin();
4194
4195 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4196 $sql .= " description = '".$this->db->escape(empty($this->description) ? $this->desc : $this->description)."'";
4197 $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
4198 $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
4199 $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
4200 $sql .= ", pu_ht = ".price2num($this->subprice);
4201 $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
4202 $sql .= ", qty = ".price2num($this->qty);
4203 $sql .= ", remise_percent = ".price2num($this->remise_percent);
4204 if ($this->fk_remise_except > 0) {
4205 $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
4206 } else {
4207 $sql .= ", fk_remise_except=null";
4208 }
4209 $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4210 $sql .= ", tva_tx = ".price2num($this->tva_tx);
4211 $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
4212 $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
4213 $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
4214 $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
4215 $sql .= ", total_ht = ".price2num($this->total_ht);
4216 $sql .= ", tva= ".price2num($this->total_tva);
4217 $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
4218 $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
4219 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4220 $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
4221 $sql .= ", product_type = ".((int) $this->product_type);
4222 $sql .= ", info_bits = ".((int) $this->info_bits);
4223 $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
4224
4225 if (!empty($this->rang)) {
4226 $sql .= ", rang=".((int) $this->rang);
4227 }
4228
4229 // Multicurrency
4230 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4231 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4232 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4233 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4234
4235 $sql .= " WHERE rowid = ".((int) $this->id);
4236
4237 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4238 $resql = $this->db->query($sql);
4239
4240 if (!$resql) {
4241 $this->db->rollback();
4242 $this->error = $this->db->lasterror();
4243 return -1;
4244 }
4245
4246 $this->rowid = $this->id;
4247 $error = 0;
4248
4249 if (!$error) {
4250 $result = $this->insertExtraFields();
4251 if ($result < 0) {
4252 $error++;
4253 }
4254 }
4255
4256 if (!$error && !$notrigger) {
4257 global $langs, $user;
4258
4259 // Call trigger
4260 if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
4261 $this->db->rollback();
4262 return -1;
4263 }
4264 // End call triggers
4265 }
4266
4267 if ($error) {
4268 $this->db->rollback();
4269 return -1;
4270 }
4271
4272 $this->db->commit();
4273 return 1;
4274 }
4275
4283 public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
4284 {
4285 global $user, $langs;
4286
4287 $error = 0;
4288
4289 dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4290
4291 // Clean parameters
4292 $this->desc = trim($this->desc);
4293 if (empty($this->tva_tx)) {
4294 $this->tva_tx = 0;
4295 }
4296 if (empty($this->localtax1_tx)) {
4297 $this->localtax1_tx = 0;
4298 }
4299 if (empty($this->localtax2_tx)) {
4300 $this->localtax2_tx = 0;
4301 }
4302 if (empty($this->localtax1_type)) {
4303 $this->localtax1_type = 0.0;
4304 }
4305 if (empty($this->localtax2_type)) {
4306 $this->localtax2_type = 0.0;
4307 }
4308 if (empty($this->total_tva)) {
4309 $this->total_tva = 0;
4310 }
4311 if (empty($this->total_localtax1)) {
4312 $this->total_localtax1 = 0;
4313 }
4314 if (empty($this->total_localtax2)) {
4315 $this->total_localtax2 = 0;
4316 }
4317 if (empty($this->rang)) {
4318 $this->rang = 0;
4319 }
4320 if (empty($this->remise_percent)) {
4321 $this->remise_percent = 0;
4322 }
4323 if (empty($this->info_bits)) {
4324 $this->info_bits = 0;
4325 }
4326 if (empty($this->subprice)) {
4327 $this->subprice = 0;
4328 }
4329 if (empty($this->special_code)) {
4330 $this->special_code = 0;
4331 }
4332 if (empty($this->fk_parent_line)) {
4333 $this->fk_parent_line = 0;
4334 }
4335 if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
4336 $this->situation_percent = 100;
4337 }
4338
4339 if (empty($this->pa_ht)) {
4340 $this->pa_ht = 0;
4341 }
4342 if (empty($this->multicurrency_subprice)) {
4343 $this->multicurrency_subprice = 0;
4344 }
4345 if (empty($this->multicurrency_total_ht)) {
4346 $this->multicurrency_total_ht = 0;
4347 }
4348 if (empty($this->multicurrency_total_tva)) {
4349 $this->multicurrency_total_tva = 0;
4350 }
4351 if (empty($this->multicurrency_total_ttc)) {
4352 $this->multicurrency_total_ttc = 0;
4353 }
4354
4355
4356 // Check parameters
4357 if ($this->product_type < 0) {
4358 $this->error = 'ErrorProductTypeMustBe0orMore';
4359 return -1;
4360 }
4361 if (!empty($this->fk_product) && $this->fk_product > 0) {
4362 // Check product exists
4363 $result = Product::isExistingObject('product', $this->fk_product);
4364 if ($result <= 0) {
4365 $this->error = 'ErrorProductIdDoesNotExists';
4366 return -1;
4367 }
4368 }
4369
4370 $this->db->begin();
4371
4372 // Insertion dans base de la ligne
4373 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
4374 $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
4375 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4376 $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
4377 $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
4378 $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
4379 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4380 $sql .= ')';
4381 $sql .= " VALUES (".$this->fk_facture_fourn.",";
4382 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4383 $product_label
4384 = !empty($this->product_label)
4385 ? $this->product_label :
4386 (!empty($this->label) ? $this->label : null);
4387 $sql .= " ".(!empty($product_label) ? "'".$this->db->escape($product_label)."'" : "null").",";
4388 $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
4389 $sql .= " '".$this->db->escape($this->ref_supplier)."',";
4390 $sql .= " ".price2num($this->qty).",";
4391
4392 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4393 $sql .= " ".price2num($this->tva_tx).",";
4394 $sql .= " ".price2num($this->localtax1_tx).",";
4395 $sql .= " ".price2num($this->localtax2_tx).",";
4396 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4397 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4398 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4399 $sql .= " ".((int) $this->product_type).",";
4400 $sql .= " ".price2num($this->remise_percent).",";
4401 $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
4402 $sql .= " ".price2num($this->subprice).",";
4403 $sql .= " ".(!empty($this->qty) ? price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
4404 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4405 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4406 $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
4407 $sql .= ' '.((int) $this->rang).',';
4408 $sql .= ' '.((int) $this->special_code).',';
4409 $sql .= " ".((int) $this->info_bits).",";
4410 $sql .= " ".price2num($this->total_ht).",";
4411 $sql .= " ".price2num($this->total_tva).",";
4412 $sql .= " ".price2num($this->total_ttc).",";
4413 $sql .= " ".price2num($this->total_localtax1).",";
4414 $sql .= " ".price2num($this->total_localtax2);
4415 $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4416 $sql .= ", ".(int) $this->fk_multicurrency;
4417 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4418 $sql .= ", ".price2num($this->multicurrency_subprice);
4419 $sql .= ", ".price2num($this->multicurrency_total_ht);
4420 $sql .= ", ".price2num($this->multicurrency_total_tva);
4421 $sql .= ", ".price2num($this->multicurrency_total_ttc);
4422 $sql .= ')';
4423
4424 $resql = $this->db->query($sql);
4425 if ($resql) {
4426 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4427 $this->rowid = $this->id; // backward compatibility
4428
4429 if (!$error) {
4430 $result = $this->insertExtraFields();
4431 if ($result < 0) {
4432 $error++;
4433 }
4434 }
4435
4436 // Si fk_remise_except defini, on lie la remise a la facture
4437 // ce qui la flague comme "consommee".
4438 if ($this->fk_remise_except) {
4439 $discount = new DiscountAbsolute($this->db);
4440 $result = $discount->fetch($this->fk_remise_except);
4441 if ($result >= 0) {
4442 // Check if discount was found
4443 if ($result > 0) {
4444 // Check if discount not already affected to another invoice
4445 if ($discount->fk_facture_line > 0) {
4446 if (empty($noerrorifdiscountalreadylinked)) {
4447 $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4448 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4449 $this->db->rollback();
4450 return -3;
4451 }
4452 } else {
4453 $result = $discount->link_to_invoice($this->id, 0);
4454 if ($result < 0) {
4455 $this->error = $discount->error;
4456 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4457 $this->db->rollback();
4458 return -3;
4459 }
4460 }
4461 } else {
4462 $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4463 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4464 $this->db->rollback();
4465 return -3;
4466 }
4467 } else {
4468 $this->error = $discount->error;
4469 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4470 $this->db->rollback();
4471 return -3;
4472 }
4473 }
4474
4475 if (!$error && !$notrigger) {
4476 // Call trigger
4477 $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4478 if ($result < 0) {
4479 $this->db->rollback();
4480 return -2;
4481 }
4482 // End call triggers
4483 }
4484
4485 if (!$error) {
4486 $this->db->commit();
4487 return $this->id;
4488 }
4489
4490 foreach ($this->errors as $errmsg) {
4491 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4492 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4493 }
4494
4495 $this->db->rollback();
4496 return -1 * $error;
4497 } else {
4498 $this->error = $this->db->error();
4499 $this->db->rollback();
4500 return -2;
4501 }
4502 }
4503
4504 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4510 public function update_total()
4511 {
4512 // phpcs:enable
4513 $this->db->begin();
4514
4515 // Mise a jour ligne en base
4516 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4517 $sql .= " total_ht = ".price2num($this->total_ht);
4518 $sql .= ", tva= ".price2num($this->total_tva);
4519 $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4520 $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4521 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4522 $sql .= " WHERE rowid = ".((int) $this->id);
4523
4524 dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4525
4526 $resql = $this->db->query($sql);
4527 if ($resql) {
4528 $this->db->commit();
4529 return 1;
4530 } else {
4531 $this->error = $this->db->error();
4532 $this->db->rollback();
4533 return -2;
4534 }
4535 }
4536}
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:637
$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:655
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
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:137
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:2015
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:2015