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