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->mode_reglement_id)) {
1225 $this->mode_reglement_id = (int) $this->mode_reglement_id;
1226 }
1227 if (isset($this->cond_reglement_id)) {
1228 $this->cond_reglement_id = (int) $this->cond_reglement_id;
1229 }
1230 if (isset($this->note_private)) {
1231 $this->note_private = trim($this->note_private);
1232 $this->note = $this->note_private;
1233 }
1234 if (isset($this->note_public)) {
1235 $this->note_public = trim($this->note_public);
1236 }
1237 if (isset($this->model_pdf)) {
1238 $this->model_pdf = trim($this->model_pdf);
1239 }
1240 if (isset($this->import_key)) {
1241 $this->import_key = trim($this->import_key);
1242 }
1243
1244
1245 // Check parameters
1246 // Put here code to add control on parameters values
1247
1248 // Update request
1249 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
1250 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1251 $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1252 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1253 $sql .= " entity=".(isset($this->entity) ? ((int) $this->entity) : "null").",";
1254 $sql .= " type=".(isset($this->type) ? ((int) $this->type) : "null").",";
1255 $sql .= " subtype=".((int) $this->subtype).",";
1256 $sql .= " fk_soc=".(isset($this->socid) ? ((int) $this->socid) : "null").",";
1257 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1258 $sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
1259 if (dol_strlen($this->tms) != 0) {
1260 $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1261 }
1262 $sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
1263 $sql .= " paye=".(isset($this->paid) ? ((int) $this->paid) : "0").",";
1264 $sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
1265 $sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
1266 $sql .= " localtax1=".(isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null").",";
1267 $sql .= " localtax2=".(isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null").",";
1268 $sql .= " total_ht=".(isset($this->total_ht) ? ((float) $this->total_ht) : "null").",";
1269 $sql .= " total_tva=".(isset($this->total_tva) ? ((float) $this->total_tva) : "null").",";
1270 $sql .= " total_ttc=".(isset($this->total_ttc) ? ((float) $this->total_ttc) : "null").",";
1271 $sql .= " fk_statut=".(isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")).",";
1272 $sql .= " fk_user_author=".(isset($this->author) ? ((int) $this->author) : "null").",";
1273 $sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null").",";
1274 $sql .= " fk_facture_source=".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
1275 $sql .= " vat_reverse_charge = ".($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0).",";
1276 $sql .= " fk_projet=".(!empty($this->fk_project) ? ((int) $this->fk_project) : "null").",";
1277 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? ((int) $this->mode_reglement_id) : "null").",";
1278 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null").",";
1279 $sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
1280 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1281 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1282 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1283 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1284 $sql .= " WHERE rowid=".((int) $this->id);
1285
1286 $this->db->begin();
1287
1288 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1289 $resql = $this->db->query($sql);
1290
1291 if (!$resql) {
1292 $error++;
1293
1294 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1295 $this->errors[] = $langs->trans('ErrorRefAlreadyExists');
1296 } else {
1297 $this->errors[] = "Error ".$this->db->lasterror();
1298 }
1299 }
1300
1301 if (!$error) {
1302 $result = $this->insertExtraFields();
1303 if ($result < 0) {
1304 $error++;
1305 }
1306 }
1307
1308 if (!$error) {
1309 if (!$notrigger) {
1310 // Call trigger
1311 $result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user);
1312 if ($result < 0) {
1313 $error++;
1314 }
1315 // End call triggers
1316 }
1317 }
1318
1319 // Commit or rollback
1320 if ($error) {
1321 foreach ($this->errors as $errmsg) {
1322 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1323 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1324 }
1325 $this->db->rollback();
1326 return -1 * $error;
1327 } else {
1328 $this->db->commit();
1329 return 1;
1330 }
1331 }
1332
1333 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1340 public function insert_discount($idremise)
1341 {
1342 // phpcs:enable
1343 global $conf, $langs;
1344
1345 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1346 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1347
1348 $this->db->begin();
1349
1350 $remise = new DiscountAbsolute($this->db);
1351 $result = $remise->fetch($idremise);
1352
1353 if ($result > 0) {
1354 if ($remise->fk_invoice_supplier) { // Protection against multiple submission
1355 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1356 $this->db->rollback();
1357 return -5;
1358 }
1359
1360 $facligne = new SupplierInvoiceLine($this->db);
1361 $facligne->fk_facture_fourn = $this->id;
1362 $facligne->fk_remise_except = $remise->id;
1363 $facligne->desc = $remise->description; // Description ligne
1364 $facligne->vat_src_code = $remise->vat_src_code;
1365 $facligne->tva_tx = $remise->tva_tx;
1366 $facligne->subprice = -$remise->amount_ht;
1367 $facligne->fk_product = 0; // Id produit predefini
1368 $facligne->product_type = 0;
1369 $facligne->qty = 1;
1370 $facligne->remise_percent = 0;
1371 $facligne->rang = -1;
1372 $facligne->info_bits = 2;
1373
1374 if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) {
1375 $facligne->rang = 1;
1376 $linecount = count($this->lines);
1377 for ($ii = 1; $ii <= $linecount; $ii++) {
1378 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
1379 }
1380 }
1381
1382 // Get buy/cost price of invoice that is source of discount
1383 if ($remise->fk_invoice_supplier_source > 0) {
1384 $srcinvoice = new FactureFournisseur($this->db);
1385 $srcinvoice->fetch($remise->fk_invoice_supplier_source);
1386 $totalcostpriceofinvoice = 0;
1387 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1388 $formmargin = new FormMargin($this->db);
1389 $arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1390 $facligne->pa_ht = $arraytmp['pa_total'];
1391 }
1392
1393 $facligne->total_ht = -$remise->amount_ht;
1394 $facligne->total_tva = -$remise->amount_tva;
1395 $facligne->total_ttc = -$remise->amount_ttc;
1396
1397 $facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1398 $facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1399 $facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1400 $facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1401
1402 $lineid = $facligne->insert();
1403 if ($lineid > 0) {
1404 $result = $this->update_price(1);
1405 if ($result > 0) {
1406 // Create link between discount and invoice line
1407 $result = $remise->link_to_invoice($lineid, 0);
1408 if ($result < 0) {
1409 $this->error = $remise->error;
1410 $this->db->rollback();
1411 return -4;
1412 }
1413
1414 $this->db->commit();
1415 return 1;
1416 } else {
1417 $this->error = $facligne->error;
1418 $this->db->rollback();
1419 return -1;
1420 }
1421 } else {
1422 $this->error = $facligne->error;
1423 $this->db->rollback();
1424 return -2;
1425 }
1426 } else {
1427 $this->db->rollback();
1428 return -3;
1429 }
1430 }
1431
1432
1440 public function delete(User $user, $notrigger = 0)
1441 {
1442 global $conf;
1443
1444 $rowid = $this->id;
1445
1446 dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1447
1448 // TODO Test if there is at least on payment. If yes, refuse to delete.
1449
1450 $error = 0;
1451 $this->db->begin();
1452
1453 if (!$error && !$notrigger) {
1454 // Call trigger
1455 $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1456 if ($result < 0) {
1457 $this->db->rollback();
1458 return -1;
1459 }
1460 // Fin appel triggers
1461 }
1462
1463 if (!$error) {
1464 // If invoice was converted into a discount not yet consumed, we remove discount
1465 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1466 $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid);
1467 $sql .= ' AND fk_invoice_supplier_line IS NULL';
1468 $resql = $this->db->query($sql);
1469
1470 // If invoice has consumned discounts
1471 $this->fetch_lines();
1472 $list_rowid_det = array();
1473 foreach ($this->lines as $key => $invoiceline) {
1474 $list_rowid_det[] = $invoiceline->id;
1475 }
1476
1477 // Consumned discounts are freed
1478 if (count($list_rowid_det)) {
1479 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1480 $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1481 $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')';
1482
1483 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1484 if (!$this->db->query($sql)) {
1485 $error++;
1486 }
1487 }
1488 }
1489
1490 if (!$error) {
1491 $main = MAIN_DB_PREFIX.'facture_fourn_det';
1492 $ef = $main."_extrafields";
1493 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_facture_fourn = ".((int) $rowid).")";
1494 $resqlef = $this->db->query($sqlef);
1495 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.((int) $rowid);
1496 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1497 $resql = $this->db->query($sql);
1498 if ($resqlef && $resql) {
1499 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.((int) $rowid);
1500 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1501 $resql2 = $this->db->query($sql);
1502 if (!$resql2) {
1503 $error++;
1504 }
1505 } else {
1506 $error++;
1507 }
1508 }
1509
1510 if (!$error) {
1511 // Delete linked object
1512 $res = $this->deleteObjectLinked();
1513 if ($res < 0) {
1514 $error++;
1515 }
1516 }
1517
1518 if (!$error) {
1519 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1520 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1521 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1522
1523 // We remove directory
1524 if ($conf->fournisseur->facture->dir_output) {
1525 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1526
1527 $ref = dol_sanitizeFileName($this->ref);
1528 $dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1529 $file = $dir."/".$ref.".pdf";
1530 if (file_exists($file)) {
1531 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
1532 $this->error = 'ErrorFailToDeleteFile';
1533 $error++;
1534 }
1535 }
1536 if (file_exists($dir)) {
1537 $res = @dol_delete_dir_recursive($dir);
1538
1539 if (!$res) {
1540 $this->error = 'ErrorFailToDeleteDir';
1541 $error++;
1542 }
1543 }
1544 }
1545 }
1546
1547 // Remove extrafields
1548 if (!$error) {
1549 $result = $this->deleteExtraFields();
1550 if ($result < 0) {
1551 $error++;
1552 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1553 }
1554 }
1555
1556 if (!$error) {
1557 dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1558 $this->db->commit();
1559 return 1;
1560 } else {
1561 $this->error = $this->db->lasterror();
1562 $this->db->rollback();
1563 return -$error;
1564 }
1565 }
1566
1567
1568 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1579 public function set_paid($user, $close_code = '', $close_note = '')
1580 {
1581 // phpcs:enable
1582 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
1583 return $this->setPaid($user, $close_code, $close_note);
1584 }
1585
1594 public function setPaid($user, $close_code = '', $close_note = '')
1595 {
1596 $error = 0;
1597
1598 if ($this->paid != 1) {
1599 $this->db->begin();
1600
1601 $now = dol_now();
1602
1603 dol_syslog("FactureFournisseur::setPaid", LOG_DEBUG);
1604
1605 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1606 $sql .= ' fk_statut = '.self::STATUS_CLOSED;
1607 if (!$close_code) {
1608 $sql .= ', paye=1';
1609 }
1610 if ($close_code) {
1611 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1612 }
1613 if ($close_note) {
1614 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1615 }
1616 $sql .= ', fk_user_closing = '.((int) $user->id);
1617 $sql .= ", date_closing = '".$this->db->idate($now)."'";
1618 $sql .= ' WHERE rowid = '.((int) $this->id);
1619
1620 $resql = $this->db->query($sql);
1621 if ($resql) {
1622 // Call trigger
1623 $result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1624 if ($result < 0) {
1625 $error++;
1626 }
1627 // End call triggers
1628 } else {
1629 $error++;
1630 $this->error = $this->db->error();
1631 dol_print_error($this->db);
1632 }
1633
1634 if (!$error) {
1635 $this->db->commit();
1636 return 1;
1637 } else {
1638 $this->db->rollback();
1639 return -1;
1640 }
1641 } else {
1642 return 0;
1643 }
1644 }
1645
1646 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1657 public function set_unpaid($user)
1658 {
1659 // phpcs:enable
1660 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1661 return $this->setUnpaid($user);
1662 }
1663
1672 public function setUnpaid($user)
1673 {
1674 $error = 0;
1675
1676 $this->db->begin();
1677
1678 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1679 $sql .= ' SET paye=0, fk_statut='.self::STATUS_VALIDATED.', close_code=null, close_note=null,';
1680 $sql .= ' date_closing=null,';
1681 $sql .= ' fk_user_closing=null';
1682 $sql .= ' WHERE rowid = '.((int) $this->id);
1683
1684 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1685 $resql = $this->db->query($sql);
1686 if ($resql) {
1687 // Call trigger
1688 $result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1689 if ($result < 0) {
1690 $error++;
1691 }
1692 // End call triggers
1693 } else {
1694 $error++;
1695 $this->error = $this->db->error();
1696 dol_print_error($this->db);
1697 }
1698
1699 if (!$error) {
1700 $this->db->commit();
1701 return 1;
1702 } else {
1703 $this->db->rollback();
1704 return -1;
1705 }
1706 }
1707
1718 public function setCanceled($user, $close_code = '', $close_note = '')
1719 {
1720 dol_syslog(get_class($this)."::setCanceled rowid=".((int) $this->id), LOG_DEBUG);
1721
1722 $this->db->begin();
1723
1724 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn SET';
1725 $sql .= ' fk_statut='.self::STATUS_ABANDONED;
1726 if ($close_code) {
1727 $sql .= ", close_code='".$this->db->escape($close_code)."'";
1728 }
1729 if ($close_note) {
1730 $sql .= ", close_note='".$this->db->escape($close_note)."'";
1731 }
1732 $sql .= " WHERE rowid = ".((int) $this->id);
1733
1734 $resql = $this->db->query($sql);
1735 if ($resql) {
1736 // Bound discounts are deducted from the invoice
1737 // as they have not been used since the invoice is abandoned.
1738 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1739 $sql .= ' SET fk_invoice_supplier = NULL';
1740 $sql .= ' WHERE fk_invoice_supplier = '.((int) $this->id);
1741
1742 $resql = $this->db->query($sql);
1743 if ($resql) {
1744 // Call trigger
1745 $result = $this->call_trigger('BILL_SUPPLIER_CANCEL', $user);
1746 if ($result < 0) {
1747 $this->db->rollback();
1748 return -1;
1749 }
1750 // End call triggers
1751
1752 $this->db->commit();
1753 return 1;
1754 } else {
1755 $this->error = $this->db->error()." sql=".$sql;
1756 $this->db->rollback();
1757 return -1;
1758 }
1759 } else {
1760 $this->error = $this->db->error()." sql=".$sql;
1761 $this->db->rollback();
1762 return -2;
1763 }
1764 }
1765
1775 public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1776 {
1777 global $mysoc, $conf, $langs;
1778
1779 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1780
1781 $now = dol_now();
1782
1783 $error = 0;
1784 dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1785
1786 // Force to have object complete for checks
1787 $this->fetch_thirdparty();
1788 $this->fetch_lines();
1789
1790 // Check parameters
1791 if ($this->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management)
1792 dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1793 return 0;
1794 }
1795 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier)) {
1796 $langs->load("errors");
1797 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1798 return -1;
1799 }
1800 if (count($this->lines) <= 0) {
1801 $langs->load("errors");
1802 $this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1803 return -1;
1804 }
1805
1806 // Check for mandatory fields in thirdparty (defined into setup)
1807 if (!empty($this->thirdparty) && is_object($this->thirdparty)) {
1808 $array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER');
1809 foreach ($array_to_check as $key) {
1810 $keymin = strtolower($key);
1811 if ($keymin == 'accountancy_code_supplier') {
1812 $keymin = 'code_compta_fournisseur';
1813 }
1814 if (!property_exists($this->thirdparty, $keymin)) {
1815 continue;
1816 }
1817 $vallabel = $this->thirdparty->$keymin;
1818
1819 $i = (int) preg_replace('/[^0-9]/', '', $key);
1820 if ($i > 0) {
1821 if ($this->thirdparty->isACompany()) {
1822 // Check for mandatory prof id (but only if country is other than ours)
1823 if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) {
1824 $idprof_mandatory = 'SOCIETE_'.$key.'_INVOICE_MANDATORY';
1825 if (!$vallabel && getDolGlobalString($idprof_mandatory)) {
1826 $langs->load("errors");
1827 $this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId'.$i, $this->thirdparty->country_code)).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1828 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1829 return -1;
1830 }
1831 }
1832 }
1833 } else {
1834 if ($key == 'EMAIL') {
1835 // Check for mandatory
1836 if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) {
1837 $langs->load("errors");
1838 $this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email).' ('.$langs->trans("ForbiddenBySetupRules").') ['.$langs->trans('Company').' : '.$this->thirdparty->name.']';
1839 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1840 return -1;
1841 }
1842 } elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') {
1843 // Check for mandatory
1844 if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) {
1845 $langs->load("errors");
1846 $this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name).' ('.$langs->trans("ForbiddenBySetupRules").')';
1847 dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
1848 return -1;
1849 }
1850 }
1851 }
1852 }
1853 }
1854
1855 $this->db->begin();
1856
1857 // Define new ref
1858 if ($force_number) {
1859 $num = $force_number;
1860 } elseif (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1861 $num = $this->getNextNumRef($this->thirdparty);
1862 } else {
1863 $num = $this->ref;
1864 }
1865 $this->newref = dol_sanitizeFileName($num);
1866
1867 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1868 $sql .= " SET ref='".$this->db->escape($num)."', fk_statut = 1, fk_user_valid = ".((int) $user->id).", date_valid = '".$this->db->idate($now)."'";
1869 $sql .= " WHERE rowid = ".((int) $this->id);
1870
1871 dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1872 $resql = $this->db->query($sql);
1873 if ($resql) {
1874 // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1875 if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
1876 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1877 $langs->load("agenda");
1878
1879 $cpt = count($this->lines);
1880 for ($i = 0; $i < $cpt; $i++) {
1881 if ($this->lines[$i]->fk_product > 0) {
1882 $mouvP = new MouvementStock($this->db);
1883 $mouvP->origin = &$this;
1884 $mouvP->setOrigin($this->element, $this->id);
1885 // We increase stock for product
1886 $up_ht_disc = $this->lines[$i]->subprice;
1887 if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1888 $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1889 }
1891 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1892 } else {
1893 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1894 }
1895 if ($result < 0) {
1896 $this->error = $mouvP->error;
1897 if (count($mouvP->errors)) {
1898 $this->errors = $mouvP->errors;
1899 }
1900 return -2;
1901 }
1902 }
1903 }
1904 }
1905
1906 // Triggers call
1907 if (!$error && empty($notrigger)) {
1908 // Call trigger
1909 $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1910 if ($result < 0) {
1911 $error++;
1912 }
1913 // End call triggers
1914 }
1915
1916 if (!$error) {
1917 $this->oldref = $this->ref;
1918
1919 // Rename directory if dir was a temporary ref
1920 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1921 // Now we rename also files into index
1922 $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)."'";
1923 $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;
1924 $resql = $this->db->query($sql);
1925 if (!$resql) {
1926 $error++;
1927 $this->error = $this->db->lasterror();
1928 }
1929 $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)."'";
1930 $sql .= " WHERE filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1931 $resql = $this->db->query($sql);
1932 if (!$resql) {
1933 $error++;
1934 $this->error = $this->db->lasterror();
1935 }
1936
1937 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1938 $oldref = dol_sanitizeFileName($this->ref);
1939 $dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1940 $dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->newref;
1941 if (!$error && file_exists($dirsource)) {
1942 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1943
1944 if (@rename($dirsource, $dirdest)) {
1945 dol_syslog("Rename ok");
1946 // Rename docs starting with $oldref with $this->newref
1947 $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, '/'));
1948 foreach ($listoffiles as $fileentry) {
1949 $dirsource = $fileentry['name'];
1950 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $this->newref, $dirsource);
1951 $dirsource = $fileentry['path'].'/'.$dirsource;
1952 $dirdest = $fileentry['path'].'/'.$dirdest;
1953 @rename($dirsource, $dirdest);
1954 }
1955 }
1956 }
1957 }
1958 }
1959
1960 // Set new ref and define current statut
1961 if (!$error) {
1962 $this->ref = $this->newref;
1965 //$this->date_validation=$now; this is stored into log table
1966 }
1967
1968 if (!$error) {
1969 $this->db->commit();
1970 return 1;
1971 } else {
1972 $this->db->rollback();
1973 return -1;
1974 }
1975 } else {
1976 $this->error = $this->db->error();
1977 $this->db->rollback();
1978 return -1;
1979 }
1980 }
1981
1990 public function setDraft($user, $idwarehouse = -1, $notrigger = 0)
1991 {
1992 // phpcs:enable
1993 global $conf, $langs;
1994
1995 $error = 0;
1996
1997 if ($this->status == self::STATUS_DRAFT) {
1998 dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1999 return 0;
2000 }
2001
2002 dol_syslog(__METHOD__, LOG_DEBUG);
2003
2004 $this->db->begin();
2005
2006 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
2007 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2008 $sql .= " WHERE rowid = ".((int) $this->id);
2009
2010 $result = $this->db->query($sql);
2011 if ($result) {
2012 if (!$error) {
2013 $this->oldcopy = clone $this;
2014 }
2015
2016 // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
2017 if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) {
2018 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2019 $langs->load("agenda");
2020
2021 $cpt = count($this->lines);
2022 for ($i = 0; $i < $cpt; $i++) {
2023 if ($this->lines[$i]->fk_product > 0) {
2024 $mouvP = new MouvementStock($this->db);
2025 $mouvP->origin = &$this;
2026 $mouvP->setOrigin($this->element, $this->id);
2027 // We increase stock for product
2029 $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2030 } else {
2031 $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
2032 }
2033 }
2034 }
2035 }
2036 // Triggers call
2037 if (!$error && empty($notrigger)) {
2038 // Call trigger
2039 $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
2040 if ($result < 0) {
2041 $error++;
2042 }
2043 // End call triggers
2044 }
2045 if ($error == 0) {
2046 $this->db->commit();
2047 return 1;
2048 } else {
2049 $this->db->rollback();
2050 return -1;
2051 }
2052 } else {
2053 $this->error = $this->db->error();
2054 $this->db->rollback();
2055 return -1;
2056 }
2057 }
2058
2059
2093 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)
2094 {
2095 global $langs, $mysoc;
2096
2097 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);
2098 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2099
2100 if ($this->status == self::STATUS_DRAFT) {
2101 // Clean parameters
2102 if (empty($remise_percent)) {
2103 $remise_percent = 0;
2104 }
2105 if (empty($qty)) {
2106 $qty = 0;
2107 }
2108 if (empty($info_bits)) {
2109 $info_bits = 0;
2110 }
2111 if (empty($rang)) {
2112 $rang = 0;
2113 }
2114 if (empty($fk_code_ventilation)) {
2115 $fk_code_ventilation = 0;
2116 }
2117 if (empty($txtva)) {
2118 $txtva = 0;
2119 }
2120 if (empty($txlocaltax1)) {
2121 $txlocaltax1 = 0;
2122 }
2123 if (empty($txlocaltax2)) {
2124 $txlocaltax2 = 0;
2125 }
2126
2127 $remise_percent = price2num($remise_percent);
2128 $qty = price2num($qty);
2129 $pu = price2num($pu);
2130 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
2131 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
2132 }
2133 $txlocaltax1 = price2num($txlocaltax1);
2134 $txlocaltax2 = price2num($txlocaltax2);
2135
2136 if ($date_start && $date_end && $date_start > $date_end) {
2137 $langs->load("errors");
2138 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2139 return -1;
2140 }
2141
2142 $this->db->begin();
2143
2144 if ($fk_product > 0) {
2145 if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) {
2146 // Check quantity is enough
2147 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
2148 $prod = new ProductFournisseur($this->db);
2149 if ($prod->fetch($fk_product) > 0) {
2150 $product_type = $prod->type;
2151 $label = $prod->label;
2152 $fk_prod_fourn_price = 0;
2153
2154 // We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
2155 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2156 $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
2157 if ($result > 0) {
2158 if (empty($pu)) {
2159 $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2160 }
2161 $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2162 // is remise percent not keyed but present for the product we add it
2163 if ($remise_percent == 0 && $prod->remise_percent != 0) {
2164 $remise_percent = $prod->remise_percent;
2165 }
2166 }
2167 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
2168 $langs->load("errors");
2169 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2170 $this->db->rollback();
2171 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
2172 //$pu = $prod->fourn_pu; // We do not overwrite unit price
2173 //$ref = $prod->ref_fourn; // We do not overwrite ref supplier price
2174 return -1;
2175 }
2176 if ($result == -1) {
2177 $langs->load("errors");
2178 $this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
2179 $this->db->rollback();
2180 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
2181 return -1;
2182 }
2183 if ($result < -1) {
2184 $this->error = $prod->error;
2185 $this->db->rollback();
2186 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
2187 return -1;
2188 }
2189 } else {
2190 $this->error = $prod->error;
2191 $this->db->rollback();
2192 return -1;
2193 }
2194 }
2195 } else {
2196 $product_type = $type;
2197 }
2198
2199 if (isModEnabled("multicurrency") && $pu_devise > 0) {
2200 $pu = 0;
2201 }
2202
2203 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2204
2205 // Clean vat code
2206 $reg = array();
2207 $vat_src_code = '';
2208 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
2209 $vat_src_code = $reg[1];
2210 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
2211 }
2212
2213 // Calcul du total TTC et de la TVA pour la ligne a partir de
2214 // qty, pu, remise_percent et txtva
2215 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2216 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2217
2218 $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);
2219 $total_ht = $tabprice[0];
2220 $total_tva = $tabprice[1];
2221 $total_ttc = $tabprice[2];
2222 $total_localtax1 = $tabprice[9];
2223 $total_localtax2 = $tabprice[10];
2224 $pu_ht = $tabprice[3];
2225
2226 // MultiCurrency
2227 $multicurrency_total_ht = $tabprice[16];
2228 $multicurrency_total_tva = $tabprice[17];
2229 $multicurrency_total_ttc = $tabprice[18];
2230 $pu_ht_devise = $tabprice[19];
2231
2232 // Check parameters
2233 if ($type < 0) {
2234 return -1;
2235 }
2236
2237 if ($rang < 0) {
2238 $rangmax = $this->line_max();
2239 $rang = $rangmax + 1;
2240 }
2241
2242 // Insert line
2243 $supplierinvoiceline = new SupplierInvoiceLine($this->db);
2244
2245 $supplierinvoiceline->context = $this->context;
2246
2247 $supplierinvoiceline->fk_facture_fourn = $this->id;
2248 //$supplierinvoiceline->label=$label; // deprecated
2249 $supplierinvoiceline->desc = $desc;
2250 $supplierinvoiceline->ref_supplier = $ref_supplier;
2251
2252 $supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2253 $supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2254
2255 $supplierinvoiceline->vat_src_code = $vat_src_code;
2256 $supplierinvoiceline->tva_tx = $txtva;
2257 $supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2258 $supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2259 $supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2260 $supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2261
2262 $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
2263 $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
2264 $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
2265 $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
2266 $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
2267
2268 $supplierinvoiceline->fk_product = $fk_product;
2269 $supplierinvoiceline->product_type = $type;
2270 $supplierinvoiceline->remise_percent = $remise_percent;
2271 $supplierinvoiceline->date_start = $date_start;
2272 $supplierinvoiceline->date_end = $date_end;
2273 $supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation;
2274 $supplierinvoiceline->rang = $rang;
2275 $supplierinvoiceline->info_bits = $info_bits;
2276 $supplierinvoiceline->fk_remise_except = $fk_remise_except;
2277
2278
2279 $supplierinvoiceline->special_code = (int) $special_code;
2280 $supplierinvoiceline->fk_parent_line = $fk_parent_line;
2281 $supplierinvoiceline->origin = $this->origin;
2282 $supplierinvoiceline->origin_id = $origin_id;
2283 $supplierinvoiceline->fk_unit = $fk_unit;
2284
2285 // Multicurrency
2286 $supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency;
2287 $supplierinvoiceline->multicurrency_code = $this->multicurrency_code;
2288 $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
2289
2290 $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
2291 $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
2292 $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
2293
2294 if (is_array($array_options) && count($array_options) > 0) {
2295 $supplierinvoiceline->array_options = $array_options;
2296 }
2297
2298 $result = $supplierinvoiceline->insert($notrigger);
2299 if ($result > 0) {
2300 // Reorder if child line
2301 if (!empty($fk_parent_line)) {
2302 $this->line_order(true, 'DESC');
2303 } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2304 $linecount = count($this->lines);
2305 for ($ii = $rang; $ii <= $linecount; $ii++) {
2306 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2307 }
2308 }
2309
2310 // Mise a jour information denormalisees au niveau de la facture meme
2311 $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.
2312 if ($result > 0) {
2313 $this->db->commit();
2314 return $supplierinvoiceline->id;
2315 } else {
2316 $this->error = $this->db->error();
2317 $this->db->rollback();
2318 return -1;
2319 }
2320 } else {
2321 $this->error = $supplierinvoiceline->error;
2322 $this->errors = $supplierinvoiceline->errors;
2323 $this->db->rollback();
2324 return -2;
2325 }
2326 } else {
2327 return 0;
2328 }
2329 }
2330
2356 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)
2357 {
2358 global $mysoc, $langs;
2359
2360 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);
2361 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2362
2363 $pu = price2num($pu);
2364 $qty = price2num($qty);
2365 $remise_percent = (float) price2num($remise_percent);
2366 $pu_devise = price2num($pu_devise);
2367
2368 // Check parameters
2369 //if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
2370 if ($type < 0) {
2371 return -1;
2372 }
2373
2374 if ($date_start && $date_end && $date_start > $date_end) {
2375 $langs->load("errors");
2376 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2377 return -1;
2378 }
2379
2380 // Clean parameters
2381 if (empty($vatrate)) {
2382 $vatrate = 0;
2383 }
2384 if (empty($txlocaltax1)) {
2385 $txlocaltax1 = 0;
2386 }
2387 if (empty($txlocaltax2)) {
2388 $txlocaltax2 = 0;
2389 }
2390
2391 $txlocaltax1 = (float) price2num($txlocaltax1);
2392 $txlocaltax2 = (float) price2num($txlocaltax2);
2393
2394 // Calcul du total TTC et de la TVA pour la ligne a partir de
2395 // qty, pu, remise_percent et txtva
2396 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2397 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2398
2399 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
2400
2401 $reg = array();
2402
2403 // Clean vat code
2404 $vat_src_code = '';
2405 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2406 $vat_src_code = $reg[1];
2407 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2408 }
2409
2410 $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);
2411 $total_ht = $tabprice[0];
2412 $total_tva = $tabprice[1];
2413 $total_ttc = $tabprice[2];
2414 $pu_ht = $tabprice[3];
2415 $pu_tva = $tabprice[4];
2416 $pu_ttc = $tabprice[5];
2417 $total_localtax1 = $tabprice[9];
2418 $total_localtax2 = $tabprice[10];
2419
2420 // MultiCurrency
2421 $multicurrency_total_ht = $tabprice[16];
2422 $multicurrency_total_tva = $tabprice[17];
2423 $multicurrency_total_ttc = $tabprice[18];
2424 $pu_ht_devise = $tabprice[19];
2425
2426 if (empty($info_bits)) {
2427 $info_bits = 0;
2428 }
2429
2430 //Fetch current line from the database and then clone the object and set it in $oldline property
2431 $line = new SupplierInvoiceLine($this->db);
2432 $line->fetch($id);
2433 $line->fetch_optionals();
2434
2435 $staticline = clone $line;
2436
2437 if ($idproduct) {
2438 $product = new Product($this->db);
2439 $result = $product->fetch($idproduct);
2440 $product_type = $product->type;
2441 } else {
2442 $idproduct = $staticline->fk_product;
2443 $product_type = $type;
2444 }
2445
2446 $line->oldline = $staticline;
2447 $line->context = $this->context;
2448
2449 $line->description = $desc;
2450 $line->desc = $desc;
2451
2452 $line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative
2453 $line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
2454 $line->pu_ht = $line->subprice; // deprecated
2455 $line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise
2456
2457 $line->remise_percent = $remise_percent;
2458 $line->ref_supplier = $ref_supplier;
2459
2460 $line->date_start = $date_start;
2461 $line->date_end = $date_end;
2462
2463 $line->vat_src_code = $vat_src_code;
2464 $line->tva_tx = $vatrate;
2465 $line->localtax1_tx = $txlocaltax1;
2466 $line->localtax2_tx = $txlocaltax2;
2467 $line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0];
2468 $line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2];
2469
2470 $line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht);
2471 $line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva);
2472 $line->total_localtax1 = $total_localtax1;
2473 $line->total_localtax2 = $total_localtax2;
2474 $line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc);
2475
2476 $line->fk_product = $idproduct;
2477 $line->product_type = $product_type;
2478 $line->info_bits = $info_bits;
2479 $line->fk_unit = $fk_unit;
2480 $line->rang = $rang;
2481
2482 if (is_array($array_options) && count($array_options) > 0) {
2483 // We replace values in this->line->array_options only for entries defined into $array_options
2484 foreach ($array_options as $key => $value) {
2485 $line->array_options[$key] = $array_options[$key];
2486 }
2487 }
2488
2489 // Multicurrency
2490 $line->multicurrency_subprice = $pu_ht_devise;
2491 $line->multicurrency_total_ht = $multicurrency_total_ht;
2492 $line->multicurrency_total_tva = $multicurrency_total_tva;
2493 $line->multicurrency_total_ttc = $multicurrency_total_ttc;
2494
2495 $res = $line->update($notrigger);
2496
2497 if ($res < 1) {
2498 $this->errors[] = $line->error;
2499 $this->errors = array_merge($this->errors, $line->errors);
2500 } else {
2501 // Update total price into invoice record
2502 $res = $this->update_price('1', 'auto', 0, $this->thirdparty);
2503 }
2504
2505 return $res;
2506 }
2507
2515 public function deleteLine($rowid, $notrigger = 0)
2516 {
2517 if (!$rowid) {
2518 $rowid = $this->id;
2519 }
2520
2521 $this->db->begin();
2522
2523 // Free the discount linked to a line of invoice
2524 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2525 $sql .= ' SET fk_invoice_supplier_line = NULL';
2526 $sql .= ' WHERE fk_invoice_supplier_line = '.((int) $rowid);
2527
2528 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2529 $result = $this->db->query($sql);
2530 if (!$result) {
2531 $this->error = $this->db->error();
2532 $this->db->rollback();
2533 return -2;
2534 }
2535
2536 $line = new SupplierInvoiceLine($this->db);
2537
2538 if ($line->fetch($rowid) < 1) {
2539 return -1;
2540 }
2541
2542 $res = $line->delete($notrigger);
2543
2544 if ($res < 1) {
2545 $this->errors[] = $line->error;
2546 $this->db->rollback();
2547 return -3;
2548 } else {
2549 $res = $this->update_price(1);
2550
2551 if ($res > 0) {
2552 $this->db->commit();
2553 return 1;
2554 } else {
2555 $this->db->rollback();
2556 $this->error = $this->db->lasterror();
2557 return -4;
2558 }
2559 }
2560 }
2561
2562
2569 public function info($id)
2570 {
2571 $sql = 'SELECT c.rowid, datec, tms as datem, ';
2572 $sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2573 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2574 $sql .= ' WHERE c.rowid = '.((int) $id);
2575
2576 $result = $this->db->query($sql);
2577 if ($result) {
2578 if ($this->db->num_rows($result)) {
2579 $obj = $this->db->fetch_object($result);
2580
2581 $this->id = $obj->rowid;
2582
2583 $this->user_creation_id = $obj->fk_user_author;
2584 $this->user_validation_id = $obj->fk_user_valid;
2585 $this->user_modification_id = $obj->fk_user_modif;
2586 $this->date_creation = $this->db->jdate($obj->datec);
2587 $this->date_modification = $this->db->jdate($obj->datem);
2588 //$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)
2589 }
2590 $this->db->free($result);
2591 } else {
2592 dol_print_error($this->db);
2593 }
2594 }
2595
2596 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2605 public function list_replacable_supplier_invoices($socid = 0)
2606 {
2607 // phpcs:enable
2608 global $conf;
2609
2610 $return = array();
2611
2612 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2613 $sql .= " ff.rowid as rowidnext";
2614 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2615 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2616 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2617 $sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2618 $sql .= " AND f.entity = ".$conf->entity;
2619 $sql .= " AND f.paye = 0"; // Pas classee payee completement
2620 $sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2621 $sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement
2622 if ($socid > 0) {
2623 $sql .= " AND f.fk_soc = ".((int) $socid);
2624 }
2625 $sql .= " ORDER BY f.ref";
2626
2627 dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2628 $resql = $this->db->query($sql);
2629 if ($resql) {
2630 while ($obj = $this->db->fetch_object($resql)) {
2631 $return[$obj->rowid] = array(
2632 'id' => $obj->rowid,
2633 'ref' => $obj->ref,
2634 'status' => $obj->fk_statut
2635 );
2636 }
2637 //print_r($return);
2638 return $return;
2639 } else {
2640 $this->error = $this->db->error();
2641 return -1;
2642 }
2643 }
2644
2645 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2655 public function list_qualified_avoir_supplier_invoices($socid = 0)
2656 {
2657 // phpcs:enable
2658 global $conf;
2659
2660 $return = array();
2661
2662 $sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn";
2663 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2664 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2665 $sql .= " WHERE f.entity = ".$conf->entity;
2666 $sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2667 $sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2668 $sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2669 $sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2670 if ($socid > 0) {
2671 $sql .= " AND f.fk_soc = ".((int) $socid);
2672 }
2673 $sql .= " ORDER BY f.ref";
2674
2675 dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2676 $resql = $this->db->query($sql);
2677 if ($resql) {
2678 while ($obj = $this->db->fetch_object($resql)) {
2679 $qualified = 0;
2680 if ($obj->fk_statut == self::STATUS_VALIDATED) {
2681 $qualified = 1;
2682 }
2683 if ($obj->fk_statut == self::STATUS_CLOSED) {
2684 $qualified = 1;
2685 }
2686 if ($qualified) {
2687 $paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2688 $return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot);
2689 }
2690 }
2691
2692 return $return;
2693 } else {
2694 $this->error = $this->db->error();
2695 return -1;
2696 }
2697 }
2698
2699 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2706 public function load_board($user)
2707 {
2708 // phpcs:enable
2709 global $conf, $langs;
2710
2711 $sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc';
2712 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2713 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
2714 $sql .= " JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2715 }
2716 $sql .= ' WHERE ff.paye = 0';
2717 $sql .= " AND ff.fk_statut IN (".self::STATUS_VALIDATED.")";
2718 $sql .= " AND ff.entity = ".$conf->entity;
2719 if ($user->socid) {
2720 $sql .= ' AND ff.fk_soc = '.((int) $user->socid);
2721 }
2722
2723 $resql = $this->db->query($sql);
2724 if ($resql) {
2725 $langs->load("bills");
2726 $now = dol_now();
2727
2728 $response = new WorkboardResponse();
2729 $response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2730 $response->label = $langs->trans("SupplierBillsToPay");
2731 $response->labelShort = $langs->trans("StatusToPay");
2732
2733 $response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills';
2734 $response->img = img_object($langs->trans("Bills"), "bill");
2735
2736 $facturestatic = new FactureFournisseur($this->db);
2737
2738 while ($obj = $this->db->fetch_object($resql)) {
2739 $facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2740 $facturestatic->statut = $obj->status; // For backward compatibility
2741 $facturestatic->status = $obj->status;
2742
2743 $response->nbtodo++;
2744 $response->total += $obj->total_ht;
2745
2746 if ($facturestatic->hasDelay()) {
2747 $response->nbtodolate++;
2748 $response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills';
2749 }
2750 }
2751
2752 $this->db->free($resql);
2753 return $response;
2754 } else {
2755 dol_print_error($this->db);
2756 $this->error = $this->db->error();
2757 return -1;
2758 }
2759 }
2760
2768 public function getTooltipContentArray($params)
2769 {
2770 global $conf, $langs, $mysoc;
2771
2772 $langs->load('bills');
2773
2774 $datas = [];
2775 $moretitle = $params['moretitle'] ?? '';
2776
2777 $picto = $this->picto;
2778 if ($this->type == self::TYPE_REPLACEMENT) {
2779 $picto .= 'r'; // Replacement invoice
2780 }
2781 if ($this->type == self::TYPE_CREDIT_NOTE) {
2782 $picto .= 'a'; // Credit note
2783 }
2784 if ($this->type == self::TYPE_DEPOSIT) {
2785 $picto .= 'd'; // Deposit invoice
2786 }
2787
2788 $datas['picto'] = img_picto('', $picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2789 if ($this->type == self::TYPE_REPLACEMENT) {
2790 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2791 } elseif ($this->type == self::TYPE_CREDIT_NOTE) {
2792 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2793 } elseif ($this->type == self::TYPE_DEPOSIT) {
2794 $datas['picto'] .= '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2795 }
2796 if (isset($this->status)) {
2797 $alreadypaid = -1;
2798 if (isset($this->alreadypaid)) {
2799 $alreadypaid = $this->alreadypaid;
2800 }
2801
2802 $datas['picto'] .= ' '.$this->getLibStatut(5, $alreadypaid);
2803 }
2804 if ($moretitle) {
2805 $datas['picto'] .= ' - '.$moretitle;
2806 }
2807 if (!empty($this->ref)) {
2808 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2809 }
2810 if (!empty($this->ref_supplier)) {
2811 $datas['refsupplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2812 }
2813 if (!empty($this->label)) {
2814 $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2815 }
2816 if (!empty($this->date)) {
2817 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2818 }
2819 if (!empty($this->date_echeance)) {
2820 $datas['date_echeance'] = '<br><b>'.$langs->trans('DateDue').':</b> '.dol_print_date($this->date_echeance, 'day');
2821 }
2822 if (!empty($this->total_ht)) {
2823 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2824 }
2825 if (!empty($this->total_tva)) {
2826 $datas['totaltva'] = '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2827 }
2828 if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) {
2829 // We keep test != 0 because $this->total_localtax1 can be '0.00000000'
2830 $datas['amountlt1'] = '<br><b>'.$langs->transcountry('AmountLT1', $mysoc->country_code).':</b> '.price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency);
2831 }
2832 if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) {
2833 $datas['amountlt2'] = '<br><b>'.$langs->transcountry('AmountLT2', $mysoc->country_code).':</b> '.price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency);
2834 }
2835 if (!empty($this->revenuestamp)) {
2836 $datas['amountrevenustamp'] = '<br><b>'.$langs->trans('RevenueStamp').':</b> '.price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency);
2837 }
2838 if (!empty($this->total_ttc)) {
2839 $datas['totalttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2840 }
2841 return $datas;
2842 }
2843
2857 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2858 {
2859 global $langs, $conf, $user, $hookmanager;
2860
2861 $result = '';
2862
2863 if ($option == 'withdraw') {
2864 $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2865 } elseif ($option == 'document') {
2866 $url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2867 } else {
2868 $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2869 }
2870
2871 if ($short) {
2872 return $url;
2873 }
2874
2875 if ($option !== 'nolink') {
2876 // Add param to save lastsearch_values or not
2877 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2878 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2879 $add_save_lastsearch_values = 1;
2880 }
2881 if ($add_save_lastsearch_values) {
2882 $url .= '&save_lastsearch_values=1';
2883 }
2884 }
2885
2886 $picto = $this->picto;
2887 if ($this->type == self::TYPE_REPLACEMENT) {
2888 $picto .= 'r'; // Replacement invoice
2889 }
2890 if ($this->type == self::TYPE_CREDIT_NOTE) {
2891 $picto .= 'a'; // Credit note
2892 }
2893 if ($this->type == self::TYPE_DEPOSIT) {
2894 $picto .= 'd'; // Deposit invoice
2895 }
2896
2897 $params = [
2898 'id' => $this->id,
2899 'objecttype' => $this->element,
2900 'option' => $option,
2901 'moretitle' => $moretitle,
2902 ];
2903 $classfortooltip = 'classfortooltip';
2904 $dataparams = '';
2905 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2906 $classfortooltip = 'classforajaxtooltip';
2907 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2908 $label = '';
2909 } else {
2910 $label = implode($this->getTooltipContentArray($params));
2911 }
2912
2913 $ref = $this->ref;
2914 if (empty($ref)) {
2915 $ref = $this->id;
2916 }
2917
2918 $linkclose = '';
2919 if (empty($notooltip)) {
2920 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2921 $label = $langs->trans("ShowSupplierInvoice");
2922 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2923 }
2924 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2925 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2926 }
2927
2928 $linkstart = '<a href="'.$url.'"';
2929 $linkstart .= $linkclose.'>';
2930 $linkend = '</a>';
2931
2932 $result .= $linkstart;
2933 if ($withpicto) {
2934 $result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
2935 }
2936 if ($withpicto != 2) {
2937 $result .= ($max ? dol_trunc($ref, $max) : $ref);
2938 }
2939 $result .= $linkend;
2940
2941 if ($addlinktonotes) {
2942 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2943 if ($txttoshow) {
2944 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2945 $result .= ' <span class="note inline-block">';
2946 $result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2947 $result .= img_picto('', 'note');
2948 $result .= '</a>';
2949 $result .= '</span>';
2950 }
2951 }
2952 global $action;
2953 $hookmanager->initHooks(array($this->element . 'dao'));
2954 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2955 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2956 if ($reshook > 0) {
2957 $result = $hookmanager->resPrint;
2958 } else {
2959 $result .= $hookmanager->resPrint;
2960 }
2961 return $result;
2962 }
2963
2972 public function getNextNumRef($soc, $mode = 'next')
2973 {
2974 global $db, $langs, $conf;
2975 $langs->load("orders");
2976
2977 // Clean parameters (if not defined or using deprecated value)
2978 if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) {
2979 $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2980 }
2981
2982 $mybool = false;
2983
2984 $file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php";
2985 $classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER');
2986
2987 // Include file with class
2988 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2989
2990 foreach ($dirmodels as $reldir) {
2991 $dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2992
2993 // Load file with numbering class (if found)
2994 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2995 }
2996
2997 if (!$mybool) {
2998 dol_print_error(null, "Failed to include file ".$file);
2999 return '';
3000 }
3001
3002 $obj = new $classname();
3003 '@phan-var-force ModeleNumRefSuppliersInvoices $obj';
3004 $numref = "";
3005 $numref = $obj->getNextValue($soc, $this, $mode);
3006
3007 if ($numref != "") {
3008 return $numref;
3009 } else {
3010 $this->error = $obj->error;
3011 return -1;
3012 }
3013 }
3014
3015
3024 public function initAsSpecimen($option = '')
3025 {
3026 global $langs, $conf;
3027 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3028
3029 $now = dol_now();
3030
3031 // Load array of products prodids
3032 $num_prods = 0;
3033 $prodids = array();
3034
3035 $sql = "SELECT rowid";
3036 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3037 $sql .= " WHERE entity IN (".getEntity('product').")";
3038 $sql .= $this->db->plimit(100);
3039
3040 $resql = $this->db->query($sql);
3041 if ($resql) {
3042 $num_prods = $this->db->num_rows($resql);
3043 $i = 0;
3044 while ($i < $num_prods) {
3045 $i++;
3046 $row = $this->db->fetch_row($resql);
3047 $prodids[$i] = $row[0];
3048 }
3049 }
3050
3051 // Initialise parameters
3052 $this->id = 0;
3053 $this->ref = 'SPECIMEN';
3054 $this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
3055 $this->specimen = 1;
3056 $this->socid = 1;
3057 $this->date = $now;
3058 $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3059 $this->cond_reglement_code = 'RECEP';
3060 $this->mode_reglement_code = 'CHQ';
3061
3062 $this->note_public = 'This is a comment (public)';
3063 $this->note_private = 'This is a comment (private)';
3064
3065 $this->multicurrency_tx = 1;
3066 $this->multicurrency_code = $conf->currency;
3067
3068 $xnbp = 0;
3069 if (empty($option) || $option != 'nolines') {
3070 // Lines
3071 $nbp = 5;
3072 while ($xnbp < $nbp) {
3073 $line = new SupplierInvoiceLine($this->db);
3074 $line->desc = $langs->trans("Description")." ".$xnbp;
3075 $line->qty = 1;
3076 $line->subprice = 100;
3077 $line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice
3078 $line->price = 100;
3079 $line->tva_tx = 19.6;
3080 $line->localtax1_tx = 0;
3081 $line->localtax2_tx = 0;
3082 if ($xnbp == 2) {
3083 $line->total_ht = 50;
3084 $line->total_ttc = 59.8;
3085 $line->total_tva = 9.8;
3086 $line->remise_percent = 50;
3087 } else {
3088 $line->total_ht = 100;
3089 $line->total_ttc = 119.6;
3090 $line->total_tva = 19.6;
3091 $line->remise_percent = 0;
3092 }
3093
3094 if ($num_prods > 0) {
3095 $prodid = mt_rand(1, $num_prods);
3096 $line->fk_product = $prodids[$prodid];
3097 }
3098 $line->product_type = 0;
3099
3100 $this->lines[$xnbp] = $line;
3101
3102 $this->total_ht += $line->total_ht;
3103 $this->total_tva += $line->total_tva;
3104 $this->total_ttc += $line->total_ttc;
3105
3106 $xnbp++;
3107 }
3108 }
3109
3110 $this->total_ht = $xnbp * 100;
3111 $this->total_tva = $xnbp * 19.6;
3112 $this->total_ttc = $xnbp * 119.6;
3113
3114 return 1;
3115 }
3116
3122 public function loadStateBoard()
3123 {
3124 global $conf, $user;
3125
3126 $this->nb = array();
3127
3128 $clause = "WHERE";
3129
3130 $sql = "SELECT count(f.rowid) as nb";
3131 $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3132 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
3133 if (empty($user->socid) && !$user->hasRight("societe", "client", "voir")) {
3134 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3135 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3136 $clause = "AND";
3137 }
3138 $sql .= " ".$clause." f.entity = ".$conf->entity;
3139
3140 $resql = $this->db->query($sql);
3141 if ($resql) {
3142 while ($obj = $this->db->fetch_object($resql)) {
3143 $this->nb["supplier_invoices"] = $obj->nb;
3144 }
3145 $this->db->free($resql);
3146 return 1;
3147 } else {
3148 dol_print_error($this->db);
3149 $this->error = $this->db->error();
3150 return -1;
3151 }
3152 }
3153
3162 public function createFromClone(User $user, $fromid, $invertdetail = 0)
3163 {
3164 global $conf, $langs;
3165
3166 $error = 0;
3167
3168 $object = new FactureFournisseur($this->db);
3169
3170 $this->db->begin();
3171
3172 // Load source object
3173 $object->fetch($fromid);
3174 $object->id = 0;
3175 $object->statut = self::STATUS_DRAFT; // For backward compatibility
3176 $object->status = self::STATUS_DRAFT;
3177
3178 $object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
3179
3180 // Clear fields
3181 $object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
3182 $object->author = $user->id; // FIXME? user_validation_id is replacement for author
3183 $object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author
3184 $object->fk_facture_source = 0;
3185 $object->date_creation = '';
3186 $object->date_validation = '';
3187 $object->date = (empty($this->date) ? dol_now() : $this->date);
3188 $object->ref_client = '';
3189 $object->close_code = '';
3190 $object->close_note = '';
3191 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
3192 $object->note_private = '';
3193 $object->note_public = '';
3194 }
3195
3196 $object->date_echeance = $object->calculate_date_lim_reglement();
3197
3198 // Loop on each line of new invoice
3199 foreach ($object->lines as $i => $line) {
3200 if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
3201 unset($object->lines[$i]);
3202 }
3203 }
3204
3205 // Create clone
3206 $object->context['createfromclone'] = 'createfromclone';
3207 $result = $object->create($user);
3208
3209 // Other options
3210 if ($result < 0) {
3211 $this->error = $object->error;
3212 $this->errors = $object->errors;
3213 $error++;
3214 }
3215
3216 if (!$error) {
3217 }
3218
3219 unset($object->context['createfromclone']);
3220
3221 // End
3222 if (!$error) {
3223 $this->db->commit();
3224 return $object->id;
3225 } else {
3226 $this->db->rollback();
3227 return -1;
3228 }
3229 }
3230
3242 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3243 {
3244 global $langs;
3245
3246 $langs->load("suppliers");
3247 $outputlangs->load("products");
3248
3249 // Set the model on the model name to use
3250 if (empty($modele)) {
3251 if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) {
3252 $modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF');
3253 } else {
3254 $modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
3255 }
3256 }
3257
3258 if (empty($modele)) {
3259 return 0;
3260 } else {
3261 $modelpath = "core/modules/supplier_invoice/doc/";
3262
3263 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3264 }
3265 }
3266
3271 public function getRights()
3272 {
3273 global $user;
3274
3275 return $user->hasRight("fournisseur", "facture");
3276 }
3277
3286 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3287 {
3288 $tables = array(
3289 'facture_fourn'
3290 );
3291
3292 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3293 }
3294
3303 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3304 {
3305 $tables = array(
3306 'facture_fourn_det'
3307 );
3308
3309 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3310 }
3311
3317 public function hasDelay()
3318 {
3319 global $conf;
3320
3321 $now = dol_now();
3322
3323 if (!$this->date_echeance) {
3324 return false;
3325 }
3326
3327 $status = isset($this->status) ? $this->status : $this->statut;
3328
3329 return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
3330 }
3331
3337 public function isCreditNoteUsed()
3338 {
3339 $isUsed = false;
3340
3341 $sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source = ".((int) $this->id);
3342 $resql = $this->db->query($sql);
3343 if (!empty($resql)) {
3344 $obj = $this->db->fetch_object($resql);
3345 if (!empty($obj->fk_invoice_supplier)) {
3346 $isUsed = true;
3347 }
3348 }
3349
3350 return $isUsed;
3351 }
3359 public function getKanbanView($option = '', $arraydata = null)
3360 {
3361 global $langs;
3362
3363 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3364
3365 $picto = $this->picto;
3366 if ($this->type == self::TYPE_REPLACEMENT) {
3367 $picto .= 'r'; // Replacement invoice
3368 }
3369 if ($this->type == self::TYPE_CREDIT_NOTE) {
3370 $picto .= 'a'; // Credit note
3371 }
3372 if ($this->type == self::TYPE_DEPOSIT) {
3373 $picto .= 'd'; // Deposit invoice
3374 }
3375
3376 $return = '<div class="box-flex-item box-flex-grow-zero">';
3377 $return .= '<div class="info-box info-box-sm">';
3378 $return .= '<span class="info-box-icon bg-infobox-action">';
3379 $return .= img_picto('', $picto);
3380 $return .= '</span>';
3381 $return .= '<div class="info-box-content">';
3382 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3383 if ($selected >= 0) {
3384 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3385 }
3386 if (!empty($arraydata['thirdparty'])) {
3387 $return .= '<br><span class="info-box-label">'.$arraydata['thirdparty'].'</span>';
3388 }
3389 if (property_exists($this, 'date')) {
3390 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date, 'day').'</span>';
3391 }
3392 if (property_exists($this, 'total_ht')) {
3393 $return .= ' &nbsp; <span class="info-box-label amount" title="'.dol_escape_htmltag($langs->trans("AmountHT")).'">'.price($this->total_ht);
3394 $return .= ' '.$langs->trans("HT");
3395 $return .= '</span>';
3396 }
3397 if (method_exists($this, 'getLibStatut')) {
3398 $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
3399 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3, $alreadypaid).'</div>';
3400 }
3401 $return .= '</div>';
3402 $return .= '</div>';
3403 $return .= '</div>';
3404 return $return;
3405 }
3406
3413 public function setVATReverseCharge($vatreversecharge)
3414 {
3415 if (!$this->table_element) {
3416 dol_syslog(get_class($this)."::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR);
3417 return -1;
3418 }
3419
3420 dol_syslog(get_class($this).'::setVATReverseCharge('.$vatreversecharge.')');
3421
3422 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3423 $sql .= " SET vat_reverse_charge = ".((int) $vatreversecharge);
3424 $sql .= " WHERE rowid=".((int) $this->id);
3425
3426 if ($this->db->query($sql)) {
3427 $this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1;
3428 return 1;
3429 } else {
3430 dol_syslog(get_class($this).'::setVATReverseCharge Error ', LOG_DEBUG);
3431 $this->error = $this->db->error();
3432 return 0;
3433 }
3434 }
3435
3447 public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '')
3448 {
3449 global $conf, $langs, $user;
3450
3451 $this->output = '';
3452 $this->error = '';
3453 $nbMailSend = 0;
3454
3455 $error = 0;
3456 $errorsMsg = array();
3457
3458 $langs->load('bills');
3459
3460 if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible.
3461 $this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers'));
3462 return 0;
3463 }
3464 if (!in_array($datetouse, array('duedate', 'invoicedate'))) {
3465 $this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"';
3466 return 0;
3467 }
3468
3469 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3470 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
3471 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3472 $formmail = new FormMail($this->db);
3473
3474 $now = dol_now();
3475 $tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt');
3476
3477 $tmpinvoice = new FactureFournisseur($this->db);
3478
3479 dol_syslog(__METHOD__." start", LOG_INFO);
3480
3481 // Select all action comm reminder
3482 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."facture_fourn as f";
3483 if (!empty($paymentmode) && $paymentmode != 'all') {
3484 $sql .= ", ".MAIN_DB_PREFIX."c_paiement as cp";
3485 }
3486 $sql .= " WHERE f.paye = 0"; // Only unpaid
3487 $sql .= " AND f.fk_statut = ".self::STATUS_VALIDATED; // Only validated status
3488 if ($datetouse == 'invoicedate') {
3489 $sql .= " AND f.datef = '".$this->db->idate($tmpidate, 'gmt')."'";
3490 } else {
3491 $sql .= " AND f.date_lim_reglement = '".$this->db->idate($tmpidate, 'gmt')."'";
3492 }
3493 $sql .= " AND f.entity IN (".getEntity('supplier_invoice', 0).")"; // One batch process only one company (no sharing)
3494 if (!empty($paymentmode) && $paymentmode != 'all') {
3495 $sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '".$this->db->escape($paymentmode)."'";
3496 }
3497 // TODO Add a filter to check there is no payment started yet
3498 if ($datetouse == 'invoicedate') {
3499 $sql .= $this->db->order("datef", "ASC");
3500 } else {
3501 $sql .= $this->db->order("date_lim_reglement", "ASC");
3502 }
3503
3504 $resql = $this->db->query($sql);
3505
3506 $stmpidate = dol_print_date($tmpidate, 'day', 'gmt');
3507 if ($datetouse == 'invoicedate') {
3508 $this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate);
3509 } else {
3510 $this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate);
3511 }
3512 if (!empty($paymentmode) && $paymentmode != 'all') {
3513 $this->output .= ' ('.$langs->transnoentitiesnoconv("PaymentMode").' '.$paymentmode.')';
3514 }
3515 $this->output .= '<br>';
3516
3517 if ($resql) {
3518 while ($obj = $this->db->fetch_object($resql)) {
3519 if (!$error) {
3520 // Load event
3521 $res = $tmpinvoice->fetch($obj->id);
3522 if ($res > 0) {
3523 $tmpinvoice->fetch_thirdparty();
3524
3525 $outputlangs = new Translate('', $conf);
3526 if ($tmpinvoice->thirdparty->default_lang) {
3527 $outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang);
3528 $outputlangs->loadLangs(array("main", "suppliers"));
3529 } else {
3530 $outputlangs = $langs;
3531 }
3532
3533 // Select email template according to language of recipient
3534 $templateId = 0;
3535 $templateLabel = '';
3536 if (empty($template) || $template == 'EmailTemplateCode') {
3537 $templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)';
3538 } else {
3539 if (is_numeric($template)) {
3540 $templateId = $template;
3541 } else {
3542 $templateLabel = $template;
3543 }
3544 }
3545
3546 $arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel);
3547 if (is_numeric($arraymessage) && $arraymessage <= 0) {
3548 $langs->load("errors");
3549 $this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template);
3550 return 0;
3551 }
3552
3553 // PREPARE EMAIL
3554 $errormesg = '';
3555
3556 // Make substitution in email content
3557 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice);
3558
3559 complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice);
3560
3561 // Topic
3562 $sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1);
3563
3564 // Content
3565 $content = $outputlangs->transnoentitiesnoconv($arraymessage->content);
3566
3567 $sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1);
3568
3569 // Recipient
3570 $to = array();
3571 if ($forcerecipient) { // If a recipient was forced
3572 $to = array($forcerecipient);
3573 } else {
3574 $res = $tmpinvoice->fetch_thirdparty();
3575 $recipient = $tmpinvoice->thirdparty;
3576 if ($res > 0) {
3577 $tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL');
3578 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) {
3579 foreach ($tmparraycontact as $data_email) {
3580 if (!empty($data_email['email'])) {
3581 $to[] = $data_email['email'];
3582 }
3583 }
3584 }
3585 if (empty($to) && !empty($recipient->email)) {
3586 $to[] = $recipient->email;
3587 }
3588 if (empty($to)) {
3589 $errormesg = "Failed to send remind to thirdparty id=".$tmpinvoice->socid.". No email defined for supplier invoice or customer.";
3590 $error++;
3591 }
3592 } else {
3593 $errormesg = "Failed to load recipient with thirdparty id=".$tmpinvoice->socid;
3594 $error++;
3595 }
3596 }
3597
3598 // Sender
3599 $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
3600 if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority
3601 $from = $arraymessage->email_from;
3602 }
3603 if (empty($from)) {
3604 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
3605 $error++;
3606 }
3607
3608 if (!$error && !empty($to)) {
3609 $this->db->begin();
3610
3611 $to = implode(',', $to);
3612 if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it
3613 $to = $to.','.$arraymessage->email_to;
3614 }
3615
3616 // Errors Recipient
3617 $errors_to = $conf->global->MAIN_MAIL_ERRORS_TO;
3618
3619 $trackid = 'inv'.$tmpinvoice->id;
3620 $sendcontext = 'standard';
3621
3622 $email_tocc = '';
3623 if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it
3624 $email_tocc = $arraymessage->email_tocc;
3625 }
3626
3627 $email_tobcc = '';
3628 if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it
3629 $email_tobcc = $arraymessage->email_tobcc;
3630 }
3631
3632 // Mail Creation
3633 $cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, '');
3634
3635 // Sending Mail
3636 if ($cMailFile->sendfile()) {
3637 $nbMailSend++;
3638
3639 // Add a line into event table
3640 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3641
3642 // Insert record of emails sent
3643 $actioncomm = new ActionComm($this->db);
3644
3645 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3646 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3647 $actioncomm->contact_id = 0;
3648
3649 $actioncomm->code = 'AC_EMAIL';
3650 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' datetouse='.$datetouse.' forcerecipient='.$forcerecipient.')';
3651 $actioncomm->note_private = $sendContent;
3652 $actioncomm->fk_project = $tmpinvoice->fk_project;
3653 $actioncomm->datep = dol_now();
3654 $actioncomm->datef = $actioncomm->datep;
3655 $actioncomm->percentage = -1; // Not applicable
3656 $actioncomm->authorid = $user->id; // User saving action
3657 $actioncomm->userownerid = $user->id; // Owner of action
3658 // Fields when action is an email (content should be added into note)
3659 $actioncomm->email_msgid = $cMailFile->msgid;
3660 $actioncomm->email_subject = $sendTopic;
3661 $actioncomm->email_from = $from;
3662 $actioncomm->email_sender = '';
3663 $actioncomm->email_to = $to;
3664 //$actioncomm->email_tocc = $sendtocc;
3665 //$actioncomm->email_tobcc = $sendtobcc;
3666 //$actioncomm->email_subject = $subject;
3667 $actioncomm->errors_to = $errors_to;
3668
3669 $actioncomm->elementtype = 'invoice_supplier';
3670 $actioncomm->fk_element = $tmpinvoice->id;
3671
3672 //$actioncomm->extraparams = $extraparams;
3673
3674 $actioncomm->create($user);
3675 } else {
3676 $errormesg = $cMailFile->error.' : '.$to;
3677 $error++;
3678
3679 // Add a line into event table
3680 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3681
3682 // Insert record of emails sent
3683 $actioncomm = new ActionComm($this->db);
3684
3685 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
3686 $actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company
3687 $actioncomm->contact_id = 0;
3688
3689 $actioncomm->code = 'AC_EMAIL';
3690 $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO';
3691 $actioncomm->note_private = $errormesg;
3692 $actioncomm->fk_project = $tmpinvoice->fk_project;
3693 $actioncomm->datep = dol_now();
3694 $actioncomm->datef = $actioncomm->datep;
3695 $actioncomm->percentage = -1; // Not applicable
3696 $actioncomm->authorid = $user->id; // User saving action
3697 $actioncomm->userownerid = $user->id; // Owner of action
3698 // Fields when action is an email (content should be added into note)
3699 $actioncomm->email_msgid = $cMailFile->msgid;
3700 $actioncomm->email_from = $from;
3701 $actioncomm->email_sender = '';
3702 $actioncomm->email_to = $to;
3703 //$actioncomm->email_tocc = $sendtocc;
3704 //$actioncomm->email_tobcc = $sendtobcc;
3705 //$actioncomm->email_subject = $subject;
3706 $actioncomm->errors_to = $errors_to;
3707
3708 //$actioncomm->extraparams = $extraparams;
3709
3710 $actioncomm->create($user);
3711 }
3712
3713 $this->db->commit(); // We always commit
3714 }
3715
3716 if ($errormesg) {
3717 $errorsMsg[] = $errormesg;
3718 }
3719 } else {
3720 $errorsMsg[] = 'Failed to fetch record invoice with ID = '.$obj->id;
3721 $error++;
3722 }
3723 }
3724 }
3725 } else {
3726 $error++;
3727 }
3728
3729 if (!$error) {
3730 $this->output .= 'Nb of emails sent : '.$nbMailSend;
3731
3732 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
3733
3734 return 0;
3735 } else {
3736 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
3737
3738 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
3739
3740 return $error;
3741 }
3742 }
3743}
3744
3745
3746
3751{
3755 public $element = 'facture_fourn_det';
3756
3760 public $table_element = 'facture_fourn_det';
3761
3765 public $parent_element = 'facture_fourn';
3766
3770 public $fk_parent_attribute = 'fk_facture_fourn';
3771
3775 public $oldline;
3776
3781 public $ref;
3782
3787 public $product_ref;
3788
3794 public $ref_supplier;
3795
3800 public $product_desc;
3801
3808 public $pu_ht;
3809
3814 public $subprice;
3815
3820 public $pu_ttc;
3821
3822
3827 public $fk_facture_fourn;
3828
3834 public $label;
3835
3841 public $description;
3842
3843 public $date_start;
3844 public $date_end;
3845
3849 public $fk_code_ventilation;
3850
3854 public $skip_update_total; // Skip update price total for special lines
3855
3859 public $situation_percent;
3860
3864 public $fk_prev_id;
3865
3870 public $vat_src_code;
3871
3876 public $tva_tx;
3877
3882 public $localtax1_tx;
3883
3888 public $localtax2_tx;
3889
3894 public $qty;
3895
3900 public $remise_percent;
3901
3906 public $pa_ht;
3907
3912 public $total_ht;
3913
3918 public $total_ttc;
3919
3924 public $total_tva;
3925
3930 public $total_localtax1;
3931
3936 public $total_localtax2;
3937
3941 public $fk_product;
3942
3947 public $product_type;
3948
3953 public $product_label;
3954
3961 public $info_bits;
3962
3967 public $fk_remise_except;
3968
3972 public $fk_parent_line;
3973
3977 public $special_code;
3978
3982 public $rang;
3983
3988 public $localtax1_type;
3989
3994 public $localtax2_type;
3995
3996
4002 public function __construct($db)
4003 {
4004 $this->db = $db;
4005 }
4006
4013 public function fetch($rowid)
4014 {
4015 $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';
4016 $sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
4017 $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';
4018 $sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
4019 $sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
4020 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
4021 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
4022 $sql .= ' WHERE f.rowid = '.((int) $rowid);
4023 $sql .= ' ORDER BY f.rang, f.rowid';
4024
4025 $query = $this->db->query($sql);
4026
4027 if (!$query) {
4028 $this->errors[] = $this->db->error();
4029 return -1;
4030 }
4031
4032 if (!$this->db->num_rows($query)) {
4033 return 0;
4034 }
4035
4036 $obj = $this->db->fetch_object($query);
4037
4038 $this->id = $obj->rowid;
4039 $this->rowid = $obj->rowid;
4040 $this->fk_facture_fourn = $obj->fk_facture_fourn;
4041
4042 $this->description = $obj->line_desc;
4043 $this->desc = $obj->line_desc;
4044 $this->date_start = $this->db->jdate($obj->date_start);
4045 $this->date_end = $this->db->jdate($obj->date_end);
4046 $this->product_ref = $obj->product_ref;
4047 $this->ref_supplier = $obj->ref_supplier;
4048 $this->product_desc = $obj->product_desc;
4049
4050 $this->subprice = $obj->pu_ht;
4051 $this->pu_ht = $this->subprice;
4052 $this->pu_ttc = $obj->pu_ttc;
4053 $this->tva_tx = $obj->tva_tx;
4054 $this->localtax1_tx = $obj->localtax1_tx;
4055 $this->localtax2_tx = $obj->localtax2_tx;
4056 $this->localtax1_type = $obj->localtax1_type;
4057 $this->localtax2_type = $obj->localtax2_type;
4058
4059 $this->qty = $obj->qty;
4060 $this->remise_percent = $obj->remise_percent;
4061 $this->fk_remise_except = $obj->fk_remise_except;
4062 //$this->tva = $obj->total_tva; // deprecated
4063 $this->total_ht = $obj->total_ht;
4064 $this->total_tva = $obj->total_tva;
4065 $this->total_localtax1 = $obj->total_localtax1;
4066 $this->total_localtax2 = $obj->total_localtax2;
4067 $this->total_ttc = $obj->total_ttc;
4068 $this->fk_product = $obj->fk_product;
4069 $this->product_type = $obj->product_type;
4070 $this->product_label = $obj->product_label;
4071 $this->label = $obj->product_label;
4072 $this->info_bits = $obj->info_bits;
4073 $this->fk_parent_line = $obj->fk_parent_line;
4074 $this->special_code = $obj->special_code;
4075 $this->rang = $obj->rang;
4076 $this->fk_unit = $obj->fk_unit;
4077
4078 $this->multicurrency_subprice = $obj->multicurrency_subprice;
4079 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
4080 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
4081 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
4082
4083 $this->fetch_optionals();
4084
4085 return 1;
4086 }
4087
4094 public function delete($notrigger = 0)
4095 {
4096 global $user;
4097
4098 dol_syslog(get_class($this)."::deleteline rowid=".((int) $this->id), LOG_DEBUG);
4099
4100 $error = 0;
4101
4102 $this->db->begin();
4103
4104 if (!$notrigger) {
4105 if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
4106 $error++;
4107 }
4108 }
4109
4110 $this->deleteObjectLinked();
4111
4112 // Remove extrafields
4113 if (!$error) {
4114 $result = $this->deleteExtraFields();
4115 if ($result < 0) {
4116 $error++;
4117 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4118 }
4119 }
4120
4121 if (!$error) {
4122 // Supprime ligne
4123 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
4124 $sql .= " WHERE rowid = ".((int) $this->id);
4125 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
4126 $resql = $this->db->query($sql);
4127 if (!$resql) {
4128 $error++;
4129 $this->error = $this->db->lasterror();
4130 }
4131 }
4132
4133 if (!$error) {
4134 $this->db->commit();
4135 return 1;
4136 } else {
4137 $this->db->rollback();
4138 return -1;
4139 }
4140 }
4141
4148 public function update($notrigger = 0)
4149 {
4150 global $conf;
4151
4152 $pu = price2num($this->subprice);
4153 $qty = price2num($this->qty);
4154
4155 // Check parameters
4156 if (empty($this->qty)) {
4157 $this->qty = 0;
4158 }
4159
4160 if ($this->product_type < 0) {
4161 return -1;
4162 }
4163
4164 // Clean parameters
4165 if (empty($this->remise_percent)) {
4166 $this->remise_percent = 0;
4167 }
4168 if (empty($this->tva_tx)) {
4169 $this->tva_tx = 0;
4170 }
4171 if (empty($this->localtax1_tx)) {
4172 $this->localtax1_tx = 0;
4173 }
4174 if (empty($this->localtax2_tx)) {
4175 $this->localtax2_tx = 0;
4176 }
4177
4178 if (empty($this->pa_ht)) {
4179 $this->pa_ht = 0;
4180 }
4181 if (empty($this->multicurrency_subprice)) {
4182 $this->multicurrency_subprice = 0;
4183 }
4184 if (empty($this->multicurrency_total_ht)) {
4185 $this->multicurrency_total_ht = 0;
4186 }
4187 if (empty($this->multicurrency_total_tva)) {
4188 $this->multicurrency_total_tva = 0;
4189 }
4190 if (empty($this->multicurrency_total_ttc)) {
4191 $this->multicurrency_total_ttc = 0;
4192 }
4193
4194 $fk_product = (int) $this->fk_product;
4195 $fk_unit = (int) $this->fk_unit;
4196
4197 $this->db->begin();
4198
4199 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4200 $sql .= " description = '".$this->db->escape(empty($this->description) ? $this->desc : $this->description)."'";
4201 $sql .= ", ref = '".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
4202 $sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
4203 $sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
4204 $sql .= ", pu_ht = ".price2num($this->subprice);
4205 $sql .= ", pu_ttc = ".price2num($this->pu_ttc);
4206 $sql .= ", qty = ".price2num($this->qty);
4207 $sql .= ", remise_percent = ".price2num($this->remise_percent);
4208 if ($this->fk_remise_except > 0) {
4209 $sql .= ", fk_remise_except=".((int) $this->fk_remise_except);
4210 } else {
4211 $sql .= ", fk_remise_except=null";
4212 }
4213 $sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4214 $sql .= ", tva_tx = ".price2num($this->tva_tx);
4215 $sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
4216 $sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
4217 $sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
4218 $sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
4219 $sql .= ", total_ht = ".price2num($this->total_ht);
4220 $sql .= ", tva= ".price2num($this->total_tva);
4221 $sql .= ", total_localtax1= ".price2num($this->total_localtax1);
4222 $sql .= ", total_localtax2= ".price2num($this->total_localtax2);
4223 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4224 $sql .= ", fk_product = ".($fk_product > 0 ? (int) $fk_product : 'null');
4225 $sql .= ", product_type = ".((int) $this->product_type);
4226 $sql .= ", info_bits = ".((int) $this->info_bits);
4227 $sql .= ", fk_unit = ".($fk_unit > 0 ? (int) $fk_unit : 'null');
4228
4229 if (!empty($this->rang)) {
4230 $sql .= ", rang=".((int) $this->rang);
4231 }
4232
4233 // Multicurrency
4234 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
4235 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4236 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4237 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4238
4239 $sql .= " WHERE rowid = ".((int) $this->id);
4240
4241 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4242 $resql = $this->db->query($sql);
4243
4244 if (!$resql) {
4245 $this->db->rollback();
4246 $this->error = $this->db->lasterror();
4247 return -1;
4248 }
4249
4250 $this->rowid = $this->id;
4251 $error = 0;
4252
4253 if (!$error) {
4254 $result = $this->insertExtraFields();
4255 if ($result < 0) {
4256 $error++;
4257 }
4258 }
4259
4260 if (!$error && !$notrigger) {
4261 global $langs, $user;
4262
4263 // Call trigger
4264 if ($this->call_trigger('LINEBILL_SUPPLIER_MODIFY', $user) < 0) {
4265 $this->db->rollback();
4266 return -1;
4267 }
4268 // End call triggers
4269 }
4270
4271 if ($error) {
4272 $this->db->rollback();
4273 return -1;
4274 }
4275
4276 $this->db->commit();
4277 return 1;
4278 }
4279
4287 public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
4288 {
4289 global $user, $langs;
4290
4291 $error = 0;
4292
4293 dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
4294
4295 // Clean parameters
4296 $this->desc = trim($this->desc);
4297 if (empty($this->tva_tx)) {
4298 $this->tva_tx = 0;
4299 }
4300 if (empty($this->localtax1_tx)) {
4301 $this->localtax1_tx = 0;
4302 }
4303 if (empty($this->localtax2_tx)) {
4304 $this->localtax2_tx = 0;
4305 }
4306 if (empty($this->localtax1_type)) {
4307 $this->localtax1_type = 0.0;
4308 }
4309 if (empty($this->localtax2_type)) {
4310 $this->localtax2_type = 0.0;
4311 }
4312 if (empty($this->total_tva)) {
4313 $this->total_tva = 0;
4314 }
4315 if (empty($this->total_localtax1)) {
4316 $this->total_localtax1 = 0;
4317 }
4318 if (empty($this->total_localtax2)) {
4319 $this->total_localtax2 = 0;
4320 }
4321 if (empty($this->rang)) {
4322 $this->rang = 0;
4323 }
4324 if (empty($this->remise_percent)) {
4325 $this->remise_percent = 0;
4326 }
4327 if (empty($this->info_bits)) {
4328 $this->info_bits = 0;
4329 }
4330 if (empty($this->subprice)) {
4331 $this->subprice = 0;
4332 }
4333 if (empty($this->special_code)) {
4334 $this->special_code = 0;
4335 }
4336 if (empty($this->fk_parent_line)) {
4337 $this->fk_parent_line = 0;
4338 }
4339 if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
4340 $this->situation_percent = 100;
4341 }
4342
4343 if (empty($this->pa_ht)) {
4344 $this->pa_ht = 0;
4345 }
4346 if (empty($this->multicurrency_subprice)) {
4347 $this->multicurrency_subprice = 0;
4348 }
4349 if (empty($this->multicurrency_total_ht)) {
4350 $this->multicurrency_total_ht = 0;
4351 }
4352 if (empty($this->multicurrency_total_tva)) {
4353 $this->multicurrency_total_tva = 0;
4354 }
4355 if (empty($this->multicurrency_total_ttc)) {
4356 $this->multicurrency_total_ttc = 0;
4357 }
4358
4359
4360 // Check parameters
4361 if ($this->product_type < 0) {
4362 $this->error = 'ErrorProductTypeMustBe0orMore';
4363 return -1;
4364 }
4365 if (!empty($this->fk_product) && $this->fk_product > 0) {
4366 // Check product exists
4367 $result = Product::isExistingObject('product', $this->fk_product);
4368 if ($result <= 0) {
4369 $this->error = 'ErrorProductIdDoesNotExists';
4370 return -1;
4371 }
4372 }
4373
4374 $this->db->begin();
4375
4376 // Insertion dans base de la ligne
4377 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
4378 $sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
4379 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4380 $sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
4381 $sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
4382 $sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
4383 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4384 $sql .= ')';
4385 $sql .= " VALUES (".$this->fk_facture_fourn.",";
4386 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4387 $product_label
4388 = !empty($this->product_label)
4389 ? $this->product_label :
4390 (!empty($this->label) ? $this->label : null);
4391 $sql .= " ".(!empty($product_label) ? "'".$this->db->escape($product_label)."'" : "null").",";
4392 $sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
4393 $sql .= " '".$this->db->escape($this->ref_supplier)."',";
4394 $sql .= " ".price2num($this->qty).",";
4395
4396 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4397 $sql .= " ".price2num($this->tva_tx).",";
4398 $sql .= " ".price2num($this->localtax1_tx).",";
4399 $sql .= " ".price2num($this->localtax2_tx).",";
4400 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4401 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4402 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
4403 $sql .= " ".((int) $this->product_type).",";
4404 $sql .= " ".price2num($this->remise_percent).",";
4405 $sql .= ' '.(!empty($this->fk_remise_except) ? ((int) $this->fk_remise_except) : "null").',';
4406 $sql .= " ".price2num($this->subprice).",";
4407 $sql .= " ".(!empty($this->qty) ? price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
4408 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
4409 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
4410 $sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
4411 $sql .= ' '.((int) $this->rang).',';
4412 $sql .= ' '.((int) $this->special_code).',';
4413 $sql .= " ".((int) $this->info_bits).",";
4414 $sql .= " ".price2num($this->total_ht).",";
4415 $sql .= " ".price2num($this->total_tva).",";
4416 $sql .= " ".price2num($this->total_ttc).",";
4417 $sql .= " ".price2num($this->total_localtax1).",";
4418 $sql .= " ".price2num($this->total_localtax2);
4419 $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4420 $sql .= ", ".(int) $this->fk_multicurrency;
4421 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4422 $sql .= ", ".price2num($this->multicurrency_subprice);
4423 $sql .= ", ".price2num($this->multicurrency_total_ht);
4424 $sql .= ", ".price2num($this->multicurrency_total_tva);
4425 $sql .= ", ".price2num($this->multicurrency_total_ttc);
4426 $sql .= ')';
4427
4428 $resql = $this->db->query($sql);
4429 if ($resql) {
4430 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
4431 $this->rowid = $this->id; // backward compatibility
4432
4433 if (!$error) {
4434 $result = $this->insertExtraFields();
4435 if ($result < 0) {
4436 $error++;
4437 }
4438 }
4439
4440 // Si fk_remise_except defini, on lie la remise a la facture
4441 // ce qui la flague comme "consommee".
4442 if ($this->fk_remise_except) {
4443 $discount = new DiscountAbsolute($this->db);
4444 $result = $discount->fetch($this->fk_remise_except);
4445 if ($result >= 0) {
4446 // Check if discount was found
4447 if ($result > 0) {
4448 // Check if discount not already affected to another invoice
4449 if ($discount->fk_facture_line > 0) {
4450 if (empty($noerrorifdiscountalreadylinked)) {
4451 $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
4452 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4453 $this->db->rollback();
4454 return -3;
4455 }
4456 } else {
4457 $result = $discount->link_to_invoice($this->id, 0);
4458 if ($result < 0) {
4459 $this->error = $discount->error;
4460 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4461 $this->db->rollback();
4462 return -3;
4463 }
4464 }
4465 } else {
4466 $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
4467 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4468 $this->db->rollback();
4469 return -3;
4470 }
4471 } else {
4472 $this->error = $discount->error;
4473 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
4474 $this->db->rollback();
4475 return -3;
4476 }
4477 }
4478
4479 if (!$error && !$notrigger) {
4480 // Call trigger
4481 $result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
4482 if ($result < 0) {
4483 $this->db->rollback();
4484 return -2;
4485 }
4486 // End call triggers
4487 }
4488
4489 if (!$error) {
4490 $this->db->commit();
4491 return $this->id;
4492 }
4493
4494 foreach ($this->errors as $errmsg) {
4495 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4496 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4497 }
4498
4499 $this->db->rollback();
4500 return -1 * $error;
4501 } else {
4502 $this->error = $this->db->error();
4503 $this->db->rollback();
4504 return -2;
4505 }
4506 }
4507
4508 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4514 public function update_total()
4515 {
4516 // phpcs:enable
4517 $this->db->begin();
4518
4519 // Mise a jour ligne en base
4520 $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
4521 $sql .= " total_ht = ".price2num($this->total_ht);
4522 $sql .= ", tva= ".price2num($this->total_tva);
4523 $sql .= ", total_localtax1 = ".price2num($this->total_localtax1);
4524 $sql .= ", total_localtax2 = ".price2num($this->total_localtax2);
4525 $sql .= ", total_ttc = ".price2num($this->total_ttc);
4526 $sql .= " WHERE rowid = ".((int) $this->id);
4527
4528 dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
4529
4530 $resql = $this->db->query($sql);
4531 if ($resql) {
4532 $this->db->commit();
4533 return 1;
4534 } else {
4535 $this->error = $this->db->error();
4536 $this->db->rollback();
4537 return -2;
4538 }
4539 }
4540}
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:2037
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:2037