dolibarr 25.0.0-alpha
card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-2025 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
6 * Copyright (C) 2005-2015 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2012-2023 Christophe Battarel <christophe.battarel@altairis.fr>
10 * Copyright (C) 2012-2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11 * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
12 * Copyright (C) 2013 Jean-Francois FERRY <jfefe@aternatik.fr>
13 * Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
14 * Copyright (C) 2014-2024 Ferran Marcet <fmarcet@2byte.es>
15 * Copyright (C) 2015-2016 Marcos García <marcosgdf@gmail.com>
16 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18 * Copyright (C) 2022-2023 Solution Libre SAS <contact@solution-libre.fr>
19 * Copyright (C) 2023 Nick Fragoulis
20 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2024-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
22 * Copyright (C) 2025-2026 Lenin Rivas <lenin.rivas777@gmail.com>
23 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
24 * Copyright (C) 2026 Joachim Küter <git-jk@bloxera.com>
25 * Copyright (C) 2026 Lionel Vessiller <lvessiller@open-dsi.fr>
26 * Copyright (C) 2026 José MARTINEZ <jose.martinez@pichinov.com>
27 *
28 * This program is free software; you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation; either version 3 of the License, or
31 * (at your option) any later version.
32 *
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 *
38 * You should have received a copy of the GNU General Public License
39 * along with this program. If not, see <https://www.gnu.org/licenses/>.
40 */
41
48// Load Dolibarr environment
49require '../../main.inc.php';
59require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
60require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
61require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
62require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
63require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
64require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
65require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
66require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
67require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
68require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
69require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
70require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php';
71require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
72require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
73if (isModEnabled('order')) {
74 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
75}
76if (isModEnabled('project')) {
77 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
78 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
79}
80require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
81
82if (isModEnabled('variants')) {
83 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
84}
85if (isModEnabled('accounting')) {
86 require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
87}
88
89// Load translation files required by the page
90$langs->loadLangs(array('bills', 'companies', 'compta', 'products', 'banks', 'main', 'withdrawals'));
91if (isModEnabled('incoterm')) {
92 $langs->load('incoterm');
93}
94if (isModEnabled('margin')) {
95 $langs->load('margins');
96}
97
98// General $Variables
99$action = GETPOST('action', 'aZ09');
100$confirm = GETPOST('confirm', 'alpha');
101$cancel = GETPOST('cancel', 'alpha');
102$backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used
103$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used
104
105$id = (GETPOSTINT('id') ? GETPOSTINT('id') : GETPOSTINT('facid')); // For backward compatibility
106$ref = GETPOST('ref', 'alpha');
107$socid = GETPOSTINT('socid');
108$lineid = GETPOSTINT('lineid');
109$origin = GETPOST('origin', 'alpha');
110$originid = (GETPOSTINT('originid') ? GETPOSTINT('originid') : GETPOSTINT('origin_id')); // For backward compatibility
111$fac_rec = GETPOSTINT('fac_rec');
112$facid = GETPOSTINT('facid');
113$ref_client = GETPOST('ref_client', 'alpha');
114$inputReasonId = GETPOSTINT('input_reason_id');
115$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
116$projectid = (GETPOSTINT('projectid') ? GETPOSTINT('projectid') : 0);
117$selectedLines = GETPOST('toselect', 'array:int');
118
119// PDF
120$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
121$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
122$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
123
124// Number of lines for predefined product/service choices
125$NBLINES = 4;
126
127$object = new Facture($db);
128
129// Fetch optionals attributes and labels
130$extrafields->fetch_name_optionals_label($object->table_element);
131
132$objectidnext = 0;
133$total_global_ttc = 0;
134$displayWarranty = false;
135$statusreplacement = 0;
136$type_fac = 0;
137$price_base_type = '';
138$array_options = array();
139
140// Load object
141if ($id > 0 || !empty($ref)) {
142 if ($action != 'add') {
143 if (!getDolGlobalString('INVOICE_USE_SITUATION')) {
144 $fetch_situation = false;
145 } else {
146 $fetch_situation = true;
147 }
148 $ret = $object->fetch($id, $ref, '', 0, $fetch_situation);
149 if ($ret > 0 && isset($object->fk_project)) {
150 $ret = $object->fetchProject();
151 }
152 }
153}
154
155// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
156$hookmanager->initHooks(array('invoicecard', 'globalcard'));
157
158// Permissions
159$usercanread = $user->hasRight("facture", "lire");
160$usercancreate = $user->hasRight("facture", "creer");
161$usercanissuepayment = $user->hasRight("facture", "paiement");
162$usercandelete = $user->hasRight("facture", "supprimer") || ($usercancreate && isset($object->status) && $object->status == $object::STATUS_DRAFT);
163$usercancreatecontract = $user->hasRight("contrat", "creer");
164
165// Advanced Permissions
166$usercanvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'validate')));
167$usercansend = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercanread) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'send')));
168$usercanreopen = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'reopen')));
169if (getDolGlobalString('INVOICE_DISALLOW_REOPEN')) {
170 $usercanreopen = false;
171}
172$usercanunvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !empty($usercancreate)) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'unvalidate')));
173$usermustrespectpricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
174
175// Other permissions
176$usercancreatemargin = $user->hasRight('margins', 'creer');
177$usercanreadallmargin = $user->hasRight('margins', 'liretous');
178$usercancreatewithdrarequest = $user->hasRight('prelevement', 'bons', 'creer');
179
180$permissionnote = $usercancreate; // Used by the include of actions_setnotes.inc.php
181$permissiondellink = $usercancreate; // Used by the include of actions_dellink.inc.php
182$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdonw.inc.php
183$permissiontoadd = $usercancreate; // Used by the include of actions_addupdatedelete.inc.php
184$permissiontoeditextra = $usercancreate;
185if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
186 // For action 'update_extras', is there a specific permission set for the attribute to update
187 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
188}
189
190// retained warranty invoice available type
191$retainedWarrantyInvoiceAvailableType = array();
192if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
193 $retainedWarrantyInvoiceAvailableType = explode('+', getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY'));
194}
195
196// Security check
197if ($user->socid) {
198 $socid = $user->socid;
199}
200$isdraft = (($object->status == Facture::STATUS_DRAFT) ? 1 : 0);
201
202$result = restrictedArea($user, 'facture', $object->id, '', '', 'fk_soc', 'rowid', $isdraft);
203
204
205/*
206 * Actions
207 */
208
209$error = 0;
210
211$parameters = array('socid' => $socid);
212$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
213if ($reshook < 0) {
214 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
215}
216
217if (empty($reshook)) {
218 $backurlforlist = dolBuildUrl(DOL_URL_ROOT.'/compta/facture/list.php');
219
220 if (empty($backtopage) || ($cancel && empty($id))) {
221 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
222 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
223 $backtopage = $backurlforlist;
224 } else {
225 $backtopage = dolBuildUrl(DOL_URL_ROOT.'/compta/facture/card.php', ['id' => ((!empty($id) && $id > 0) ? $id : '__ID__')]);
226 }
227 }
228 }
229
230 if ($cancel) {
231 if (!empty($backtopageforcancel)) {
232 header("Location: ".$backtopageforcancel);
233 exit;
234 } elseif (!empty($backtopage)) {
235 header("Location: ".$backtopage);
236 exit;
237 }
238 $action = '';
239 }
240
241 include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be 'include', not 'include_once'
242
243 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
244
245 include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
246
247 // Action clone object
248 if ($action == 'confirm_clone' && $confirm == 'yes' && $permissiontoadd) {
249 if (!($socid > 0)) {
250 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('IdThirdParty')), null, 'errors');
251 } else {
252 $objectutil = dol_clone($object, 1); // We use a clone to avoid to denaturate loaded object when setting some properties for clone. We use native clone to keep this->db valid.
253 '@phan-var-force Facture $objectutil';
254
255 $objectutil->date = dol_mktime(12, 0, 0, GETPOSTINT('newdatemonth'), GETPOSTINT('newdateday'), GETPOSTINT('newdateyear'));
256 $objectutil->socid = $socid;
257 $result = $objectutil->createFromClone($user, $id, (GETPOSTISSET('entity') ? GETPOSTINT('entity') : null));
258 if ($result > 0) {
259 $warningMsgLineList = array();
260 // check all product lines are to sell otherwise add a warning message for each product line is not to sell
261 foreach ($objectutil->lines as $line) {
262 if (!is_object($line->product)) {
263 $line->fetch_product();
264 }
265 if (is_object($line->product) && $line->product->id > 0) {
266 if (empty($line->product->status)) {
267 $warningMsgLineList[$line->id] = $langs->trans('WarningLineProductNotToSell', $line->product->ref);
268 }
269 }
270 }
271 if (!empty($warningMsgLineList)) {
272 setEventMessages('', $warningMsgLineList, 'warnings');
273 }
274
275 header("Location: " . $_SERVER['PHP_SELF'] . '?facid=' . $result);
276 exit();
277 } else {
278 $langs->load("errors");
279 setEventMessages($objectutil->error, $objectutil->errors, 'errors');
280 $action = '';
281 }
282 }
283 } elseif ($action == 'reopen' && $usercanreopen) {
284 $result = $object->fetch($id);
285
286 if ($object->status == Facture::STATUS_CLOSED || ($object->status == Facture::STATUS_ABANDONED && ($object->close_code != 'replaced' || $object->getIdReplacingInvoice() == 0)) || ($object->status == Facture::STATUS_VALIDATED && $object->paye == 1)) { // ($object->status == 1 && $object->paye == 1) should not happened but can be found when data are corrupted
287 $result = $object->setUnpaid($user);
288 if ($result > 0) {
289 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
290 exit();
291 } else {
292 setEventMessages($object->error, $object->errors, 'errors');
293 }
294 }
295 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) {
296 // Delete invoice
297 $result = $object->fetch($id);
298 $object->fetch_thirdparty();
299
300 $idwarehouse = GETPOST('idwarehouse');
301
302 $qualified_for_stock_change = 0;
303 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
304 $qualified_for_stock_change = $object->hasProductsOrServices(2);
305 } else {
306 $qualified_for_stock_change = $object->hasProductsOrServices(1);
307 }
308
309 $isErasable = $object->is_erasable();
310
311 if (($isErasable > 0) || ($usercancreate && $isErasable == 1)) {
312 $result = $object->delete($user, 0, (int) $idwarehouse);
313 if ($result > 0) {
314 header('Location: '.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1');
315 exit();
316 } else {
317 setEventMessages($object->error, $object->errors, 'errors');
318 $action = '';
319 }
320 }
321 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
322 // Delete line
323 $object->fetch($id);
324 $object->fetch_thirdparty();
325
326 $result = $object->deleteLine(GETPOSTINT('lineid'));
327 if ($result > 0) {
328 // reorder lines
329 $object->line_order(true);
330 // Define output language
331 $outputlangs = $langs;
332 $newlang = '';
333 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
334 $newlang = GETPOST('lang_id');
335 }
336 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
337 $newlang = $object->thirdparty->default_lang;
338 }
339 if (!empty($newlang)) {
340 $outputlangs = new Translate("", $conf);
341 $outputlangs->setDefaultLang($newlang);
342 $outputlangs->load('products');
343 }
344 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
345 $ret = $object->fetch($id); // Reload to get new records
346 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
347 }
348 if ($result >= 0) {
349 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
350 exit();
351 }
352 } else {
353 setEventMessages($object->error, $object->errors, 'errors');
354 $action = '';
355 }
356 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) {
357 // Delete line
358 $object->fetch($id);
359 $object->fetch_thirdparty();
360
361 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'));
362 if ($result > 0) {
363 // reorder lines
364 $object->line_order(true);
365 // Define output language
366 $outputlangs = $langs;
367 $newlang = '';
368 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
369 $newlang = GETPOST('lang_id');
370 }
371 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
372 $newlang = $object->thirdparty->default_lang;
373 }
374 if (!empty($newlang)) {
375 $outputlangs = new Translate("", $conf);
376 $outputlangs->setDefaultLang($newlang);
377 $outputlangs->load('products');
378 }
379 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
380 $ret = $object->fetch($id); // Reload to get new records
381 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
382 }
383 if ($result >= 0) {
384 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
385 exit();
386 }
387 } else {
388 setEventMessages($object->error, $object->errors, 'errors');
389 $action = '';
390 }
391 } elseif ($action == 'unlinkdiscount' && $usercancreate) {
392 // Delete link of credit note to invoice
393 $discount = new DiscountAbsolute($db);
394 $result = $discount->fetch(GETPOSTINT("discountid"));
395 $discount->unlink_invoice();
396 $object->fetch($id);
397 if ($object->paye == 1 && (float) $object->getRemainToPay() > 0) {
398 $object->setUnpaid($user);
399 }
400 } elseif ($action == 'valid' && $usercancreate) {
401 // Validation
402 $object->fetch($id);
403
404 if ((preg_match('/^[\‍(]?PROV/i', $object->ref) || empty($object->ref)) && // empty should not happened, but when it occurs, the test save life
405 getDolGlobalString('FAC_FORCE_DATE_VALIDATION') // If option enabled, we force invoice date
406 ) {
407 $object->date = dol_now();
408 }
409
410 if (getDolGlobalString('INVOICE_CHECK_POSTERIOR_DATE')) {
411 $last_of_type = $object->willBeLastOfSameType(true);
412 if (empty($object->date_validation) && !$last_of_type[0]) {
413 setEventMessages($langs->transnoentities("ErrorInvoiceIsNotLastOfSameType", $object->ref, dol_print_date($object->date, 'day'), dol_print_date($last_of_type[1], 'day')), null, 'errors');
414 $action = '';
415 }
416 }
417
418 // We check invoice sign
419 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
420 // If a credit note, the sign must be negative
421 if ($object->total_ht > 0) {
422 setEventMessages($langs->trans("ErrorInvoiceAvoirMustBeNegative"), null, 'errors');
423 $action = '';
424 }
425 } else {
426 // If not a credit note, amount with tax must be positive or nul.
427 // Note that amount excluding tax can be negative because you can have a invoice of 100 with vat of 20 that
428 // consumes a credit note of 100 with vat 0 (total with tax is 0 but without tax is -20).
429 // For some cases, credit notes can have a vat of 0 (for example when selling goods in France).
430 if (!getDolGlobalString('FACTURE_ENABLE_NEGATIVE') && $object->total_ttc < 0) {
431 setEventMessages($langs->trans("ErrorInvoiceOfThisTypeMustBePositive"), null, 'errors');
432 $action = '';
433 }
434
435 // Also negative lines should not be allowed on 'non Credit notes' invoices. A test is done when adding or updating lines but we must
436 // do it again in validation to avoid cases where invoice is created from another object that allow negative lines.
437 // Note that we can accept the negative line if sum with other lines with same vat makes total positive: Because all the lines will be merged together
438 // when converted into 'available credit' and we will get a positive available credit line.
439 // Note: Other solution if you want to add a negative line on invoice, is to create a discount for customer and consumme it (but this is possible on standard invoice only).
440 $array_of_total_ht_per_vat_rate = array();
441 $array_of_total_ht_devise_per_vat_rate = array();
442 foreach ($object->lines as $line) {
443 //$vat_src_code_for_line = $line->vat_src_code; // TODO We check sign of total per vat without taking into account the vat code because for the moment the vat code is lost/unknown when we add a down payment.
444 $vat_src_code_for_line = '';
445 if (empty($array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
446 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
447 }
448 if (empty($array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
449 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
450 }
451 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->total_ht;
452 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->multicurrency_total_ht;
453 }
454
455 //var_dump($array_of_total_ht_per_vat_rate);exit;
456 foreach ($array_of_total_ht_per_vat_rate as $vatrate => $tmpvalue) {
457 $tmp_total_ht = price2num($array_of_total_ht_per_vat_rate[$vatrate]);
458 $tmp_total_ht_devise = price2num($array_of_total_ht_devise_per_vat_rate[$vatrate]);
459
460 if (($tmp_total_ht < 0 || $tmp_total_ht_devise < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
461 if ($object->type == $object::TYPE_DEPOSIT) {
462 $langs->load("errors");
463 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
464 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
465 $error++;
466 $action = '';
467 } else {
468 $tmpvatratetoshow = explode('_', $vatrate);
469 $tmpvatratetoshow[0] = round((float) $tmpvatratetoshow[0], 2);
470
471 if ($tmpvatratetoshow[0] != 0) {
472 $langs->load("errors");
473 setEventMessages($langs->trans("ErrorLinesCantBeNegativeForOneVATRate", (string) $tmpvatratetoshow[0]), null, 'errors');
474 $error++;
475 $action = '';
476 }
477 }
478 }
479 }
480 }
481 } elseif ($action == 'classin' && $usercancreate) {
482 $object->fetch($id);
483 $object->setProject(GETPOSTINT('projectid'));
484 if ($result < 0) {
485 setEventMessages($object->error, $object->errors, 'errors');
486 }
487 } elseif ($action == 'setposinfo' && $usercancreate) {
488 $object->fetch($id);
489 $object->module_source = GETPOST('posmodule');
490 $object->pos_source = GETPOST('posterminal');
491 $result = $object->update($user);
492 if ($result < 0) {
493 setEventMessages($object->error, $object->errors, 'errors');
494 }
495 } elseif ($action == 'setmode' && $usercancreate) {
496 $object->fetch($id);
497 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
498 if ($result < 0) {
499 setEventMessages($object->error, $object->errors, 'errors');
500 }
501 } elseif ($action == 'setretainedwarrantyconditions' && $usercancreate) {
503 $object->fetch($id);
504 $object->retained_warranty_fk_cond_reglement = 0; // To clean property
505 $result = $object->setRetainedWarrantyPaymentTerms(GETPOSTINT('retained_warranty_fk_cond_reglement'));
506 if ($result < 0) {
507 setEventMessages($object->error, $object->errors, 'errors');
508 }
509
510 $old_rw_date_lim_reglement = $object->retained_warranty_date_limit;
511 $new_rw_date_lim_reglement = $object->calculate_date_lim_reglement($object->retained_warranty_fk_cond_reglement);
512 if ($new_rw_date_lim_reglement > $old_rw_date_lim_reglement) {
513 $object->retained_warranty_date_limit = $new_rw_date_lim_reglement;
514 }
515 if ($object->retained_warranty_date_limit < $object->date) {
516 $object->retained_warranty_date_limit = $object->date;
517 }
518 $result = $object->update($user);
519 if ($result < 0) {
520 setEventMessages($object->error, $object->errors, 'errors');
521 }
522 } elseif ($action == 'setretainedwarranty' && $usercancreate) {
523 $object->fetch($id);
524 $result = $object->setRetainedWarranty(GETPOSTFLOAT('retained_warranty'));
525 if ($result < 0) {
526 setEventMessages($object->error, $object->errors, 'errors');
527 }
528 } elseif ($action == 'setretainedwarrantydatelimit' && $usercancreate) {
529 $object->fetch($id);
530 $result = $object->setRetainedWarrantyDateLimit(GETPOSTDATE('retained_warranty_date_limit'));
531 if ($result < 0) {
532 setEventMessages($object->error, $object->errors, 'errors');
533 }
534 } elseif ($action == 'setmulticurrencycode' && $usercancreate) { // Multicurrency Code
535 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
536 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) { // Multicurrency rate
537 $result = $object->setMulticurrencyRate(GETPOSTFLOAT('multicurrency_tx'), GETPOSTINT('calculation_mode'));
538 } elseif ($action == 'setinvoicedate' && $usercancreate) {
539 $object->fetch($id);
540 $old_date_lim_reglement = $object->date_lim_reglement;
541 $newdate = dol_mktime(0, 0, 0, GETPOSTINT('invoicedatemonth'), GETPOSTINT('invoicedateday'), GETPOSTINT('invoicedateyear'), 'tzserver');
542 if (empty($newdate)) {
543 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
544 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id.'&action=editinvoicedate&token='.newToken());
545 exit;
546 }
547 if ($newdate > (dol_now('tzuserrel') + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
548 if (!getDolGlobalString('INVOICE_MAX_FUTURE_DELAY')) {
549 setEventMessages($langs->trans("WarningInvoiceDateInFuture"), null, 'warnings');
550 } else {
551 setEventMessages($langs->trans("WarningInvoiceDateTooFarInFuture"), null, 'warnings');
552 }
553 }
554
555 $object->date = $newdate;
556 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
557 if ($new_date_lim_reglement) {
558 $object->date_lim_reglement = $new_date_lim_reglement;
559 }
560 if ($object->date_lim_reglement < $object->date) {
561 $object->date_lim_reglement = $object->date;
562 }
563 $result = $object->update($user);
564 if ($result < 0) {
565 setEventMessages($object->error, $object->errors, 'errors');
566 $action = 'editinvoicedate';
567 }
568 } elseif ($action == 'setdate_pointoftax' && $usercancreate) {
569 $object->fetch($id);
570
571 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
572
573 $object->date_pointoftax = $date_pointoftax;
574 $result = $object->update($user);
575 if ($result < 0) {
576 setEventMessages($object->error, $object->errors, 'errors');
577 }
578 } elseif ($action == 'setconditions' && $usercancreate) {
579 $object->fetch($id);
580 $object->cond_reglement_code = 0; // To clean property
581 $object->cond_reglement_id = 0; // To clean property
582
583 $db->begin();
584
585 if (!$error) {
586 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'));
587 if ($result < 0) {
588 $error++;
589 setEventMessages($object->error, $object->errors, 'errors');
590 }
591 }
592
593 if (!$error) {
594 $old_date_lim_reglement = $object->date_lim_reglement;
595 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
596 if ($new_date_lim_reglement) {
597 $object->date_lim_reglement = $new_date_lim_reglement;
598 }
599 if ($object->date_lim_reglement < $object->date) {
600 $object->date_lim_reglement = $object->date;
601 }
602 $result = $object->update($user);
603 if ($result < 0) {
604 $error++;
605 setEventMessages($object->error, $object->errors, 'errors');
606 }
607 }
608
609 if ($error) {
610 $db->rollback();
611 } else {
612 $db->commit();
613 }
614 } elseif ($action == 'setpaymentterm' && $usercancreate) {
615 $object->fetch($id);
616 $object->date_lim_reglement = dol_mktime(12, 0, 0, GETPOSTINT('paymenttermmonth'), GETPOSTINT('paymenttermday'), GETPOSTINT('paymenttermyear'));
617 if ($object->date_lim_reglement < $object->date) {
618 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
619 setEventMessages($langs->trans("DatePaymentTermCantBeLowerThanObjectDate"), null, 'warnings');
620 }
621 $result = $object->update($user);
622 if ($result < 0) {
623 setEventMessages($object->error, $object->errors, 'errors');
624 }
625 } elseif ($action == 'setrevenuestamp' && $usercancreate) {
626 $object->fetch($id);
627 $object->revenuestamp = (float) price2num(GETPOST('revenuestamp'));
628 $result = $object->update($user);
629 $object->update_price(1);
630 if ($result < 0) {
631 setEventMessages($object->error, $object->errors, 'errors');
632 } else {
633 // Define output language
634 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
635 $outputlangs = $langs;
636 $newlang = '';
637 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
638 $newlang = GETPOST('lang_id', 'aZ09');
639 }
640 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
641 $newlang = $object->thirdparty->default_lang;
642 }
643 if (!empty($newlang)) {
644 $outputlangs = new Translate("", $conf);
645 $outputlangs->setDefaultLang($newlang);
646 $outputlangs->load('products');
647 }
648 $model = $object->model_pdf;
649 $ret = $object->fetch($id); // Reload to get new records
650
651 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
652 if ($result < 0) {
653 setEventMessages($object->error, $object->errors, 'errors');
654 }
655 }
656 }
657 } elseif ($action == 'set_incoterms' && isModEnabled('incoterm') && $usercancreate) { // Set incoterm
658 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
659 if ($result < 0) {
660 setEventMessages($object->error, $object->errors, 'errors');
661 }
662 } elseif ($action == 'set_dispute_status' && $usercancreate) { // Set dispute status
663 $result = $object->setStatut(GETPOSTINT('dispute_status'), null, 'facture', 'FACTURE_MODIFY', 'dispute_status');
664 if ($result < 0) {
665 setEventMessages($object->error, $object->errors, 'errors');
666 }
667 } elseif ($action == 'settags' && isModEnabled('category') && $usercancreate) { // Set tags
668 $result = $object->setCategories(GETPOST('categories', 'array'));
669 if ($result < 0) {
670 setEventMessages($object->error, $object->errors, 'errors');
671 }
672 } elseif ($action == 'setbankaccount' && $usercancreate) { // Bank account
673 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
674 if ($result < 0) {
675 setEventMessages($object->error, $object->errors, 'errors');
676 }
677 } elseif ($action == 'setremisepercent' && $usercancreate) {
678 $object->fetch($id);
679 $result = $object->setDiscount($user, (float) price2num(GETPOST('remise_percent'), '', 2));
680 if ($result < 0) {
681 setEventMessages($object->error, $object->errors, 'errors');
682 }
683 } elseif ($action == 'setabsolutediscount' && $usercancreate) {
684 // We have POST[remise_id] (common case) xor POST[remise_id_for_payment] (with unstable oldoption)
685 $db->begin();
686
687 // We use the credit note to reduce amount of invoice
688 if (GETPOSTINT("remise_id") > 0) {
689 $ret = $object->fetch($id);
690 if ($ret > 0) {
691 $result = $object->insert_discount(GETPOSTINT("remise_id"));
692 if ($result < 0) {
693 setEventMessages($object->error, $object->errors, 'errors');
694 }
695 } else {
696 $error++;
697 setEventMessages($object->error, $object->errors, 'errors');
698 }
699
700 if (!$error) {
701 if ($object->status == Facture::STATUS_VALIDATED) {
702 $newremaintopay = $object->getRemainToPay(0);
703 if ($newremaintopay == 0) {
704 $object->setPaid($user);
705 }
706 }
707 }
708 }
709 // We use the credit note to reduce remain to pay
710 if (GETPOSTINT("remise_id_for_payment") > 0) {
711 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
712 $discount = new DiscountAbsolute($db);
713 $discount->fetch(GETPOSTINT("remise_id_for_payment"));
714
715 //var_dump($object->getRemainToPay(0));
716 //var_dump($discount->amount_ttc);exit;
717 $remaintopay = $object->getRemainToPay(0);
718 if (price2num($discount->total_ttc) > price2num($remaintopay)) {
719 // TODO Split the discount in 2 automatically
720 $error++;
721 setEventMessages($langs->trans("ErrorDiscountLargerThanRemainToPaySplitItBefore"), null, 'errors');
722 }
723
724 if (!$error) {
725 $result = $discount->link_to_invoice(0, $id);
726 if ($result < 0) {
727 $error++;
728 setEventMessages($discount->error, $discount->errors, 'errors');
729 }
730 }
731
732 if (!$error) {
733 // Only mark as paid when the invoice is already validated. On a still-draft
734 // invoice the discount link reduces the remain_to_pay but the invoice is not
735 // yet a legally issued document (ref is still PROV-...), so closing it would
736 // leave it stuck as paid+PROV (see #37744).
737 if ($object->status == Facture::STATUS_VALIDATED) {
738 $newremaintopay = $object->getRemainToPay(0);
739 if ($newremaintopay == 0) {
740 $object->setPaid($user);
741 }
742 }
743 }
744 }
745
746 if (!$error) {
747 $db->commit();
748 } else {
749 $db->rollback();
750 }
751
752 if (empty($error) && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
753 $outputlangs = $langs;
754 $newlang = '';
755 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
756 $newlang = GETPOST('lang_id', 'aZ09');
757 }
758 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
759 $object->fetch_thirdparty();
760 $newlang = $object->thirdparty->default_lang;
761 }
762 if (!empty($newlang)) {
763 $outputlangs = new Translate("", $conf);
764 $outputlangs->setDefaultLang($newlang);
765 }
766 $ret = $object->fetch($id); // Reload to get new records
767
768 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
769 if ($result < 0) {
770 setEventMessages($object->error, $object->errors, 'errors');
771 }
772 }
773 } elseif ($action == 'setref' && $usercancreate) {
774 $object->fetch($id);
775 $object->setValueFrom('ref', GETPOST('ref'), '', 0, '', '', $user, 'BILL_MODIFY');
776 if ($result < 0) {
777 setEventMessages($object->error, $object->errors, 'errors');
778 }
779 } elseif ($action == 'setref_client' && $usercancreate) {
780 $object->fetch($id);
781 $object->set_ref_client(GETPOST('ref_client', 'alpha'));
782 if ($result < 0) {
783 setEventMessages($object->error, $object->errors, 'errors');
784 }
785 } elseif ($action == 'setdemandreason' && $usercancreate) {
786 $result = $object->setInputReason($inputReasonId);
787 if ($result < 0) {
788 setEventMessages($object->error, $object->errors, 'errors');
789 }
790 } elseif ($action == 'confirm_valid' && $confirm == 'yes' && $usercanvalidate) {
791 // Classify to validated
792 $idwarehouse = GETPOSTINT('idwarehouse');
793
794 $object->fetch($id);
795 $object->fetch_thirdparty();
796
797 // Check for warehouse
798 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
799 $qualified_for_stock_change = 0;
800 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
801 $qualified_for_stock_change = $object->hasProductsOrServices(2);
802 } else {
803 $qualified_for_stock_change = $object->hasProductsOrServices(1);
804 }
805
806 if ($qualified_for_stock_change) {
807 if (!$idwarehouse || $idwarehouse == - 1) {
808 $error++;
809 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
810 $action = '';
811 }
812 }
813 }
814
815 if (!$error) {
816 $result = $object->validate($user, '', $idwarehouse);
817 if ($result >= 0) {
818 // Define output language
819 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
820 $outputlangs = $langs;
821 $newlang = '';
822 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
823 $newlang = GETPOST('lang_id', 'aZ09');
824 }
825 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
826 $newlang = $object->thirdparty->default_lang;
827 }
828 if (!empty($newlang)) {
829 $outputlangs = new Translate("", $conf);
830 $outputlangs->setDefaultLang($newlang);
831 $outputlangs->load('products');
832 }
833 $model = $object->model_pdf;
834
835 $ret = $object->fetch($id); // Reload to get new records
836
837 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
838 if ($result < 0) {
839 setEventMessages($object->error, $object->errors, 'errors');
840 }
841 }
842 } else {
843 if (count($object->errors)) {
844 setEventMessages(null, $object->errors, 'errors');
845 } else {
846 setEventMessages($object->error, $object->errors, 'errors');
847 }
848 }
849 }
850 } elseif ($action == 'confirm_modif' && $usercanunvalidate && !getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
851 // Go back to draft status (unvalidate)
852 $idwarehouse = GETPOSTINT('idwarehouse');
853
854 $object->fetch($id);
855 $object->fetch_thirdparty();
856
857 // Check parameters
858 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
859 $qualified_for_stock_change = 0;
860 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
861 $qualified_for_stock_change = $object->hasProductsOrServices(2);
862 } else {
863 $qualified_for_stock_change = $object->hasProductsOrServices(1);
864 }
865
866 if ($qualified_for_stock_change) {
867 if (!$idwarehouse || $idwarehouse == -1) {
868 $error++;
869 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
870 $action = '';
871 }
872 }
873 }
874
875 if (!$error) {
876 // We check if invoice has payments
877 $totalpaid = 0;
878 $sql = 'SELECT pf.amount';
879 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'paiement_facture as pf';
880 $sql .= ' WHERE pf.fk_facture = ' . ((int) $object->id);
881
882 $result = $db->query($sql);
883 if ($result) {
884 $i = 0;
885 $num = $db->num_rows($result);
886
887 while ($i < $num) {
888 $objp = $db->fetch_object($result);
889 $totalpaid += $objp->amount;
890 $i++;
891 }
892 } else {
893 dol_print_error($db, '');
894 }
895
896 $resteapayer = $object->total_ttc - $totalpaid;
897
898 // We check that invoice lines are transferred into accountancy
899 $ventilExportCompta = $object->getVentilExportCompta();
900
901 // We check if no payment has been made
902 if ($ventilExportCompta == 0) {
903 if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == $object->total_ttc && empty($object->paye))) {
904 // Set invoice to draft status
905 $result = $object->setDraft($user, $idwarehouse);
906
907 if ($result < 0) {
908 setEventMessages($object->error, $object->errors, 'errors');
909 } else {
910 // Define output language
911 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
912 $outputlangs = $langs;
913 $newlang = '';
914 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
915 $newlang = GETPOST('lang_id', 'aZ09');
916 }
917 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
918 $newlang = $object->thirdparty->default_lang;
919 }
920 if (!empty($newlang)) {
921 $outputlangs = new Translate("", $conf);
922 $outputlangs->setDefaultLang($newlang);
923 $outputlangs->load('products');
924 }
925 $model = $object->model_pdf;
926 $ret = $object->fetch($id); // Reload to get new records
927
928 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
929 }
930 }
931 }
932 }
933 }
934 } elseif ($action == 'confirm_paid' && $confirm == 'yes' && $usercanissuepayment) {
935 // Classify "paid"
936 $object->fetch($id);
937 $result = $object->setPaid($user);
938 if ($result < 0) {
939 setEventMessages($object->error, $object->errors, 'errors');
940 }
941 } elseif ($action == 'confirm_paid_partially' && $confirm == 'yes' && $usercanissuepayment) {
942 // Classif "paid partially"
943 $object->fetch($id);
944 $close_code = GETPOST("close_code", 'restricthtml');
945 $close_note = GETPOST("close_note", 'restricthtml');
946 if ($close_code) {
947 // if VatRefund
948 if (isModEnabled('tax') && $close_code == $object::CLOSECODE_WITHHOLDINGTAX) {
949 require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php';
950 $resteapayer = GETPOSTFLOAT("resteapayer");
951 $amount = (float) ($resteapayer > 0 ? $resteapayer * -1 : $resteapayer);
952 if ($amount < 0) {
953 $db->begin();
954 $tempTva = new Tva($db);
955 $tempTva->datev = $object->date;
956 $tempTva->datep = $object->date;
957 $tempTva->amount = $amount;
958 $tempTva->label = $langs->trans('WithholdingTax') . ' - ' . $object->ref;
959 //$tempTva->paye = 1;
960 $valid = $tempTva->getIdForLabel($tempTva->label);
961 if (!$valid) {
962 $ret = $tempTva->create($user);
963 if ($ret < 0) {
964 $error++;
965 } else {
966 $tempTva->setPaid($user);
967 }
968 if (empty($error)) {
969 $db->commit();
970 } else {
971 setEventMessages($tempTva->error, $tempTva->errors, 'errors');
972 $db->rollback();
973 }
974 } else {
975 $error++;
976 setEventMessages($langs->trans('LabelWithholdingExist'), $tempTva->errors, 'errors');
977 $db->rollback();
978 }
979 }
980 }
981 if (!$error) {
982 $result = $object->setPaid($user, $close_code, $close_note);
983 if ($result < 0) {
984 setEventMessages($object->error, $object->errors, 'errors');
985 } else {
986 $object->fetch($object->id); // Reload properties
987 }
988 }
989 } else {
990 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
991 }
992 } elseif ($action == 'confirm_canceled' && $confirm == 'yes' && $usercancreate) {
993 // Classify "abandoned"
994 $object->fetch($id);
995 $close_code = GETPOST("close_code", 'restricthtml');
996 $close_note = GETPOST("close_note", 'restricthtml');
997 if ($close_code) {
998 $result = $object->setCanceled($user, $close_code, $close_note);
999 if ($result < 0) {
1000 setEventMessages($object->error, $object->errors, 'errors');
1001 } else {
1002 $object->fetch($object->id); // Reload properties
1003 }
1004 } else {
1005 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
1006 }
1007 } elseif ($action == 'confirm_converttoreduc' && $confirm == 'yes' && $usercancreate) {
1008 // Convert to discount
1009 $object->fetch($id);
1010 $object->fetch_thirdparty();
1011 //$object->fetch_lines(); // Already done into fetch
1012
1013 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1014 $discountcheck = new DiscountAbsolute($db);
1015 $result = $discountcheck->fetch(0, $object->id);
1016
1017 $canconvert = 0;
1018 if ($object->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1019 $canconvert = 1; // we can convert deposit into discount if deposit is paid (completely, partially or not at all) and not already converted (see real condition into condition used to show button converttoreduc)
1020 }
1021 if (($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_SITUATION) && $object->paye == 0 && empty($discountcheck->id)) {
1022 $canconvert = 1; // we can convert credit note into discount if credit note is not paid back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc)
1023 }
1024
1025 if ($canconvert) {
1026 $db->begin();
1027
1028 $amount_ht = $amount_tva = $amount_ttc = $amount_localtax1 = $amount_localtax2 = array();
1029 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1030
1031 // Loop on each vat rate
1032 $i = 0;
1033 foreach ($object->lines as $line) {
1034 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9 and no need to create discount if amount is null
1035 $keyforvatrate = $line->tva_tx.($line->vat_src_code ? ' ('.$line->vat_src_code.')' : '');
1036
1037 if (!isset($amount_ht[$keyforvatrate])) {
1038 $amount_ht[$keyforvatrate] = 0;
1039 }
1040 $amount_ht[$keyforvatrate] += $line->total_ht;
1041 if (!isset($amount_tva[$keyforvatrate])) {
1042 $amount_tva[$keyforvatrate] = 0;
1043 }
1044 $amount_tva[$keyforvatrate] += $line->total_tva;
1045 if (!isset($amount_localtax1[$keyforvatrate])) {
1046 $amount_localtax1[$keyforvatrate] = 0;
1047 }
1048 $amount_localtax1[$keyforvatrate] += $line->total_localtax1;
1049 if (!isset($amount_localtax2[$keyforvatrate])) {
1050 $amount_localtax2[$keyforvatrate] = 0;
1051 }
1052 $amount_localtax2[$keyforvatrate] += $line->total_localtax2;
1053 if (!isset($amount_ttc[$keyforvatrate])) {
1054 $amount_ttc[$keyforvatrate] = 0;
1055 }
1056 $amount_ttc[$keyforvatrate] += $line->total_ttc;
1057 if (!isset($multicurrency_amount_ht[$keyforvatrate])) {
1058 $multicurrency_amount_ht[$keyforvatrate] = 0;
1059 }
1060 $multicurrency_amount_ht[$keyforvatrate] += $line->multicurrency_total_ht;
1061 if (!isset($multicurrency_amount_tva[$keyforvatrate])) {
1062 $multicurrency_amount_tva[$keyforvatrate] = 0;
1063 }
1064 $multicurrency_amount_tva[$keyforvatrate] += $line->multicurrency_total_tva;
1065 if (!isset($multicurrency_amount_ttc[$keyforvatrate])) {
1066 $multicurrency_amount_ttc[$keyforvatrate] = 0;
1067 }
1068 $multicurrency_amount_ttc[$keyforvatrate] += $line->multicurrency_total_ttc;
1069 $i++;
1070 }
1071 }
1072 '@phan-var-force array<string,float> $amount_ht
1073 @phan-var-force array<string,float> $amount_tva
1074 @phan-var-force array<string,float> $amount_ttc
1075 @phan-var-force array<string,float> $amount_localtax1
1076 @phan-var-force array<string,float> $amount_localtax2
1077 @phan-var-force array<string,float> $multicurrency_amount_ht
1078 @phan-var-force array<string,float> $multicurrency_amount_tva
1079 @phan-var-force array<string,float> $multicurrency_amount_ttc';
1080
1081 // If some payments were already done, we change the amount to pay using same prorate
1082 if (getDolGlobalString('INVOICE_ALLOW_REUSE_OF_CREDIT_WHEN_PARTIALLY_REFUNDED') && $object->type == Facture::TYPE_CREDIT_NOTE) {
1083 $alreadypaid = $object->getSommePaiement(); // This can be not 0 if we allow to create credit to reuse from credit notes partially refunded.
1084 if ($alreadypaid && abs($alreadypaid) < abs($object->total_ttc)) {
1085 $ratio = abs(($object->total_ttc - $alreadypaid) / $object->total_ttc);
1086 foreach ($amount_ht as $vatrate => $val) {
1087 $amount_ht[$vatrate] = price2num($amount_ht[$vatrate] * $ratio, 'MU');
1088 $amount_tva[$vatrate] = price2num($amount_tva[$vatrate] * $ratio, 'MU');
1089 $amount_ttc[$vatrate] = price2num($amount_ttc[$vatrate] * $ratio, 'MU');
1090 $amount_localtax1[$vatrate] = price2num($amount_localtax1[$vatrate] * $ratio, 'MU');
1091 $amount_localtax2[$vatrate] = price2num($amount_localtax2[$vatrate] * $ratio, 'MU');
1092 $multicurrency_amount_ht[$vatrate] = price2num($multicurrency_amount_ht[$vatrate] * $ratio, 'MU');
1093 $multicurrency_amount_tva[$vatrate] = price2num($multicurrency_amount_tva[$vatrate] * $ratio, 'MU');
1094 $multicurrency_amount_ttc[$vatrate] = price2num($multicurrency_amount_ttc[$vatrate] * $ratio, 'MU');
1095 }
1096 }
1097 }
1098 //var_dump($amount_ht);var_dump($amount_tva);var_dump($amount_ttc);exit;
1099
1100 // Insert one discount by VAT rate category
1101 $discount = new DiscountAbsolute($db);
1102 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1103 $discount->description = '(CREDIT_NOTE)';
1104 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
1105 $discount->description = '(DEPOSIT)';
1107 $discount->description = '(EXCESS RECEIVED)';
1108 } else {
1109 setEventMessages($langs->trans('CantConvertToReducAnInvoiceOfThisType'), null, 'errors');
1110 }
1111 $discount->fk_soc = $object->socid;
1112 $discount->socid = $object->socid;
1113 $discount->fk_facture_source = $object->id;
1114
1115 $error = 0;
1116
1117
1118 // Create a discount that is the amount of the excess received
1120 || $object->type == Facture::TYPE_DEPOSIT) {
1121 // If we have an excess received that need to create a discount in TTC without VAT
1122 $discount->description = '(EXCESS RECEIVED)';
1123
1124 // Total payments
1125 $sql = 'SELECT SUM(pf.amount) as total_paiements';
1126 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1127 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1128 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id);
1129 $sql .= ' AND pf.fk_paiement = p.rowid';
1130 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1131 $resql = $db->query($sql);
1132 if (!$resql) {
1134 }
1135
1136 $res = $db->fetch_object($resql);
1137 $total_paiements = $res->total_paiements;
1138
1139 // Total credit note and deposit
1140 $total_creditnote_and_deposit = 0;
1141 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1142 $sql .= " re.description, re.fk_facture_source";
1143 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1144 $sql .= " WHERE fk_facture = ".((int) $object->id);
1145 $resql = $db->query($sql);
1146 if (!empty($resql)) {
1147 while ($obj = $db->fetch_object($resql)) {
1148 $total_creditnote_and_deposit += $obj->amount_ttc;
1149 }
1150 } else {
1152 }
1153
1154 $discount->amount_ttc = price2num($total_paiements + $total_creditnote_and_deposit - $object->total_ttc, 'MT');
1155 $discount->amount_tva = 0;
1156 $discount->amount_ht = $discount->amount_ttc;
1157 $discount->tva_tx = 0;
1158 $discount->localtax1_tx = 0;
1159 $discount->localtax1_type = 0;
1160 $discount->localtax2_tx = 0;
1161 $discount->localtax2_type = 0;
1162 $discount->vat_src_code = '';
1163
1164 if ($discount->amount_ttc > 0) {
1165 $result = $discount->create($user);
1166 if ($result < 0) {
1167 $error++;
1168 }
1169 }
1170 }
1171
1172 // Create a discount that is the amount of the invoice
1174 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1175 $discount->description = '(CREDIT_NOTE)';
1176 } else {
1177 $discount->description = '(DEPOSIT)';
1178 }
1179
1180 foreach ($amount_ht as $tva_tx => $xxx) {
1181 // Get localtaxes from TVA tx
1182 $taxes = getTaxesFromId($tva_tx, $object->thirdparty, $mysoc, 0);
1183
1184 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1185 $discount->amount_ht = -((float) $amount_ht[$tva_tx]);
1186 $discount->amount_tva = -((float) $amount_tva[$tva_tx]);
1187 $discount->amount_ttc = -((float) $amount_ttc[$tva_tx]);
1188 $discount->total_ht = -((float) $amount_ht[$tva_tx]);
1189 $discount->total_tva = -((float) $amount_tva[$tva_tx]);
1190 $discount->total_localtax1 = -((float) $amount_localtax1[$tva_tx]);
1191 $discount->total_localtax2 = -((float) $amount_localtax2[$tva_tx]);
1192 $discount->total_ttc = -((float) $amount_ttc[$tva_tx]);
1193 $discount->multicurrency_amount_ht = -((float) $multicurrency_amount_ht[$tva_tx]);
1194 $discount->multicurrency_amount_tva = -((float) $multicurrency_amount_tva[$tva_tx]);
1195 $discount->multicurrency_amount_ttc = -((float) $multicurrency_amount_ttc[$tva_tx]);
1196 $discount->multicurrency_total_ht = -((float) $multicurrency_amount_ht[$tva_tx]);
1197 $discount->multicurrency_total_tva = -((float) $multicurrency_amount_tva[$tva_tx]);
1198 $discount->multicurrency_total_ttc = -((float) $multicurrency_amount_ttc[$tva_tx]);
1199 } else {
1200 //We keep the absolute value to be consistent with the function used to create the discount in case of deposit in the "create" function of the Payment class
1201 $discount->amount_ht = abs((float) $amount_ht[$tva_tx]);
1202 $discount->amount_tva = abs((float) $amount_tva[$tva_tx]);
1203 $discount->amount_ttc = abs((float) $amount_ttc[$tva_tx]);
1204 $discount->total_ht = abs((float) $amount_ht[$tva_tx]);
1205 $discount->total_tva = abs((float) $amount_tva[$tva_tx]);
1206 $discount->total_localtax1 = abs((float) $amount_localtax1[$tva_tx]);
1207 $discount->total_localtax2 = abs((float) $amount_localtax2[$tva_tx]);
1208 $discount->total_ttc = abs((float) $amount_ttc[$tva_tx]);
1209 $discount->multicurrency_amount_ht = abs((float) $multicurrency_amount_ht[$tva_tx]);
1210 $discount->multicurrency_amount_tva = abs((float) $multicurrency_amount_tva[$tva_tx]);
1211 $discount->multicurrency_amount_ttc = abs((float) $multicurrency_amount_ttc[$tva_tx]);
1212 $discount->multicurrency_total_ht = abs((float) $multicurrency_amount_ht[$tva_tx]);
1213 $discount->multicurrency_total_tva = abs((float) $multicurrency_amount_tva[$tva_tx]);
1214 $discount->multicurrency_total_ttc = abs((float) $multicurrency_amount_ttc[$tva_tx]);
1215 }
1216
1217 // Clean vat code
1218 $reg = array();
1219 $vat_src_code = '';
1220 if (preg_match('/\‍((.*)\‍)/', $tva_tx, $reg)) {
1221 $vat_src_code = $reg[1];
1222 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx); // Remove code into vatrate.
1223 }
1224
1225 $discount->tva_tx = abs((float) $tva_tx);
1226 $discount->localtax1_tx = $taxes['localtax1'];
1227 $discount->localtax1_type = $taxes['localtax1_type'];
1228 $discount->localtax2_tx = $taxes['localtax2'];
1229 $discount->localtax2_type = $taxes['localtax2_type'];
1230 $discount->vat_src_code = $vat_src_code;
1231
1232 $result = $discount->create($user);
1233 if ($result < 0) {
1234 $error++;
1235 break;
1236 }
1237 }
1238 }
1239
1240 if (empty($error)) {
1241 // Set invoice as paid, unless it's a down payment converted to credit without any payment received
1242 // (option DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID allows creating the discount/credit even if the down payment
1243 // has not been paid yet; in that case we must NOT mark it as paid since no payment was actually received)
1244 $skipSetPaid = ($object->type == Facture::TYPE_DEPOSIT && getDolGlobalInt('DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID') && price2num($object->getSommePaiement(), 'MT') == 0);
1245
1246 if ($skipSetPaid) {
1247 $object->fetch($object->id); // Reload properties
1248 $db->commit();
1249 } else {
1250 $result = $object->setPaid($user); // We can close the invoice. Even if we got an excess received, it is now into discounts.
1251 if ($result >= 0) {
1252 $object->fetch($object->id); // Reload properties
1253 $db->commit();
1254 } else {
1255 setEventMessages($object->error, $object->errors, 'errors');
1256 $db->rollback();
1257 }
1258 }
1259 } else {
1260 setEventMessages($discount->error, $discount->errors, 'errors');
1261 $db->rollback();
1262 }
1263 }
1264 } elseif ($action == 'confirm_delete_paiement' && $confirm == 'yes' && $usercanissuepayment) {
1265 // Delete payment
1266 $object->fetch($id);
1267 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0) {
1268 $paiement = new Paiement($db);
1269 $result = $paiement->fetch(GETPOSTINT('paiement_id'));
1270 if ($result > 0) {
1271 $result = $paiement->delete($user); // If fetch ok and found
1272 if ($result >= 0) {
1273 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
1274 exit;
1275 }
1276 }
1277 if ($result < 0) {
1278 setEventMessages($paiement->error, $paiement->errors, 'errors');
1279 }
1280 }
1281 } elseif ($action == 'add' && $usercancreate) {
1282 // Insert new invoice in database
1283 if ($socid > 0) {
1284 $object->socid = GETPOSTINT('socid');
1285 }
1286
1287 if (GETPOST('type') === '') {
1288 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1289 }
1290
1291 $db->begin();
1292
1293 $originentity = GETPOSTINT('originentity');
1294 $object->demand_reason_id = $inputReasonId;
1295
1296 // Fill array 'array_options' with data from add form
1297 $ret = $extrafields->setOptionalsFromPost(null, $object);
1298 if ($ret < 0) {
1299 $error++;
1300 }
1301
1302 $dateinvoice = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
1303 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
1304
1305 $classname = null;
1306
1307
1308 // Add a Replacement invoice
1309 if (GETPOST('type') == Facture::TYPE_REPLACEMENT) {
1310 if (empty($dateinvoice)) {
1311 $error++;
1312 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1313 $action = 'create';
1314 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1315 $error++;
1316 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1317 $action = 'create';
1318 }
1319
1320 if (!(GETPOSTINT('fac_replacement') > 0)) {
1321 $error++;
1322 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ReplaceInvoice")), null, 'errors');
1323 $action = 'create';
1324 }
1325
1326 if (!$error) {
1327 // This is a replacement invoice
1328 $result = $object->fetch(GETPOSTINT('fac_replacement'));
1329 $object->fetch_thirdparty();
1330
1331 $object->date = $dateinvoice;
1332 $object->date_pointoftax = $date_pointoftax;
1333 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1334 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1335 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1336 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1337 $object->model_pdf = GETPOST('model', 'alphanohtml');
1338 $object->fk_project = GETPOSTINT('projectid');
1339 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
1340 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1341 $object->fk_account = GETPOSTINT('fk_account');
1342 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1343 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1344 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1345 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1346
1347 // Special properties of replacement invoice
1348 $object->fk_facture_source = GETPOSTINT('fac_replacement');
1350
1351 $id = $object->createFromCurrent($user);
1352 if ($id <= 0) {
1353 setEventMessages($object->error, $object->errors, 'errors');
1354 }
1355 }
1356 }
1357
1358 // Add a Credit note invoice
1359 if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) {
1360 $sourceinvoice = GETPOSTINT('fac_avoir');
1361 if (!($sourceinvoice > 0) && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
1362 $error++;
1363 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceAvoirAskCombo")), null, 'errors');
1364 $action = 'create';
1365 }
1366
1367 if (empty($dateinvoice)) {
1368 $error++;
1369 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1370 $action = 'create';
1371 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1372 $error++;
1373 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1374 $action = 'create';
1375 }
1376
1377 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1378 $error++;
1379 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1380 $action = 'create';
1381 }
1382
1383 if (!$error) {
1384 if (!empty($originentity)) {
1385 $object->entity = $originentity;
1386 }
1387 $object->socid = GETPOSTINT('socid');
1388 $object->subtype = GETPOSTINT('subtype');
1389 $object->ref = GETPOST('ref');
1390 $object->date = $dateinvoice;
1391 $object->date_pointoftax = $date_pointoftax;
1392 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1393 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1394 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1395 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1396 $object->model_pdf = GETPOST('model');
1397 $object->fk_project = GETPOSTINT('projectid');
1398 $object->cond_reglement_id = 0; // No payment term for a credit note
1399 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1400 $object->fk_account = GETPOSTINT('fk_account');
1401 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1402 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1403 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1404 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1405
1406 // Special properties of replacement invoice
1407 $object->fk_facture_source = $sourceinvoice > 0 ? $sourceinvoice : '';
1409
1410 $facture_source = new Facture($db); // fetch origin object
1411 if ($object->fk_facture_source > 0 && $facture_source->fetch($object->fk_facture_source) > 0) {
1412 if ($facture_source->isSituationInvoice()) {
1413 $object->situation_counter = $facture_source->situation_counter;
1414 $object->situation_cycle_ref = $facture_source->situation_cycle_ref;
1415 $facture_source->fetchPreviousNextSituationInvoice();
1416 }
1417
1418 $object->pos_source = $facture_source->pos_source; // We must reuse the same than original values so correction will appear in same terminal same original invoice for conformity
1419 $object->module_source = $facture_source->module_source; // We must reuse the same than original values so correction will appear in same terminal same original invoice for conformity
1420 }
1421
1422 $id = $object->create($user);
1423 if ($id < 0) {
1424 $error++;
1425 } else {
1426 // copy internal contacts
1427 if ($object->copy_linked_contact($facture_source, 'internal') < 0) {
1428 $error++;
1429 } elseif ($facture_source->socid == $object->socid) {
1430 // copy external contacts if same company
1431 if ($object->copy_linked_contact($facture_source, 'external') < 0) {
1432 $error++;
1433 }
1434 }
1435 }
1436
1437 // NOTE: Pb with situation invoice when INVOICE_USE_SITUATION=1 (legacy mode)
1438 // NOTE: fields total on situation invoice are stored as cumulative values on total of lines (bad) but delta on invoice total
1439 // NOTE: fields total on credit note are stored as delta both on total of lines and on invoice total (good)
1440 // NOTE: fields situation_percent on situation invoice are stored as cumulative values on lines (bad)
1441 // NOTE: fields situation_percent on credit note are stored as delta on lines (good)
1442 if (GETPOSTINT('invoiceAvoirWithLines') == 1 && $id > 0) {
1443 if (!empty($facture_source->lines)) {
1444 $fk_parent_line = 0;
1445
1446 foreach ($facture_source->lines as $line) {
1447 // Extrafields
1448 if (method_exists($line, 'fetch_optionals')) {
1449 // load extrafields
1450 $line->fetch_optionals();
1451 }
1452
1453 // Reset fk_parent_line for no child products and special product
1454 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1455 $fk_parent_line = 0;
1456 }
1457
1458
1459 if ($facture_source->isSituationInvoice()) {
1460 $source_fk_prev_id = $line->fk_prev_id; // temporary storing situation invoice fk_prev_id
1461 $line->fk_prev_id = $line->id; // The new line of the new credit note we are creating must be linked to the situation invoice line it is created from
1462
1463 // The subtraction below assumes total_ht/situation_percent are stored cumulative on situation invoice lines
1464 // (INVOICE_USE_SITUATION = 1, legacy). In progressive mode (INVOICE_USE_SITUATION = 2), each line already
1465 // holds its own delta, so subtracting the previous invoice's line here would credit the wrong amount.
1466 if (getDolGlobalInt('INVOICE_USE_SITUATION') != 2 && !empty($facture_source->tab_previous_situation_invoice)) {
1467 // search the last standard invoice in cycle and the possible credit note between this last and facture_source
1468 // TODO Move this out of loop of $facture_source->lines
1469 $tab_jumped_credit_notes = array();
1470 $lineIndex = count($facture_source->tab_previous_situation_invoice) - 1;
1471 $searchPreviousInvoice = true;
1472 while ($searchPreviousInvoice) {
1473 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_SITUATION || $lineIndex < 1) {
1474 $searchPreviousInvoice = false; // find, exit;
1475 break;
1476 } else {
1477 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_CREDIT_NOTE) {
1478 $tab_jumped_credit_notes[$lineIndex] = $facture_source->tab_previous_situation_invoice[$lineIndex]->id;
1479 }
1480 $lineIndex--; // go to previous invoice in cycle
1481 }
1482 }
1483
1484 $maxPrevSituationPercent = 0;
1485 foreach ($facture_source->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
1486 if ($prevLine->id == $source_fk_prev_id) {
1487 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
1488
1489 //$line->subprice = $line->subprice - $prevLine->subprice;
1490 $line->total_ht -= $prevLine->total_ht;
1491 $line->total_tva -= $prevLine->total_tva;
1492 $line->total_ttc -= $prevLine->total_ttc;
1493 $line->total_localtax1 -= $prevLine->total_localtax1;
1494 $line->total_localtax2 -= $prevLine->total_localtax2;
1495
1496 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1497 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1498 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1499 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1500 }
1501 }
1502
1503 // prorata
1504 $line->situation_percent = $maxPrevSituationPercent - $line->situation_percent;
1505
1506 //print 'New line based on invoice id '.$facture_source->tab_previous_situation_invoice[$lineIndex]->id.' fk_prev_id='.$source_fk_prev_id.' will be fk_prev_id='.$line->fk_prev_id.' '.$line->total_ht.' '.$line->situation_percent.'<br>';
1507
1508 // If there is some credit note between last situation invoice and invoice used for credit note generation (note: credit notes are stored as delta)
1509 $maxPrevSituationPercent = 0;
1510 foreach ($tab_jumped_credit_notes as $index => $creditnoteid) {
1511 foreach ($facture_source->tab_previous_situation_invoice[$index]->lines as $prevLine) {
1512 if ($prevLine->fk_prev_id == $source_fk_prev_id) {
1513 $maxPrevSituationPercent = $prevLine->situation_percent;
1514
1515 $line->total_ht -= $prevLine->total_ht;
1516 $line->total_tva -= $prevLine->total_tva;
1517 $line->total_ttc -= $prevLine->total_ttc;
1518 $line->total_localtax1 -= $prevLine->total_localtax1;
1519 $line->total_localtax2 -= $prevLine->total_localtax2;
1520
1521 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1522 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1523 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1524 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1525 }
1526 }
1527 }
1528
1529 // prorata
1530 $line->situation_percent += $maxPrevSituationPercent;
1531
1532 //print 'New line based on invoice id '.$facture_source->tab_previous_situation_invoice[$lineIndex]->id.' fk_prev_id='.$source_fk_prev_id.' will be fk_prev_id='.$line->fk_prev_id.' '.$line->total_ht.' '.$line->situation_percent.'<br>';
1533 }
1534 }
1535
1536 $line->fk_facture = $object->id;
1537 $line->fk_parent_line = $fk_parent_line;
1538
1539 $line->subprice = -$line->subprice; // invert price for object
1540 $line->subprice_ttc = -$line->subprice_ttc; // keep the TTC entry mode with the inverted sign (no rounding drift)
1541 // $line->pa_ht = $line->pa_ht; // we chose to have buy/cost price always positive, so no revert of sign here
1542 $line->total_ht = -$line->total_ht;
1543 $line->total_tva = -$line->total_tva;
1544 $line->total_ttc = -$line->total_ttc;
1545 $line->total_localtax1 = -$line->total_localtax1;
1546 $line->total_localtax2 = -$line->total_localtax2;
1547
1548 $line->multicurrency_subprice = -$line->multicurrency_subprice;
1549 $line->multicurrency_total_ht = -$line->multicurrency_total_ht;
1550 $line->multicurrency_total_tva = -$line->multicurrency_total_tva;
1551 $line->multicurrency_total_ttc = -$line->multicurrency_total_ttc;
1552
1553 $line->context['createcreditnotefrominvoice'] = 1;
1554 $result = $line->insert(0, 1); // When creating credit note with same lines than source, we must ignore error if discount already linked
1555
1556 $object->lines[] = $line; // insert new line in current object
1557
1558 // Defined the new fk_parent_line
1559 if ($result > 0 && $line->product_type == 9) {
1560 $fk_parent_line = $result;
1561 }
1562 }
1563
1564 $object->update_price(1);
1565 }
1566 }
1567
1568 if (GETPOSTINT('invoiceAvoirWithPaymentRestAmount') == 1 && $id > 0) {
1569 if ($facture_source->fetch($object->fk_facture_source) > 0) {
1570 $totalpaid = $facture_source->getSommePaiement();
1571 $totalcreditnotes = $facture_source->getSumCreditNotesUsed();
1572 $totaldeposits = $facture_source->getSumDepositsUsed();
1573 $remain_to_pay = abs($facture_source->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits);
1574
1575 if (getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') == 'default') {
1576 if ((empty($object->thirdparty) || !is_object($object->thirdparty) || get_class($object->thirdparty) != 'Societe')) {
1577 $object->fetch_thirdparty();
1578 }
1579 if (!empty($object->thirdparty) && is_object($object->thirdparty) && get_class($object->thirdparty) == 'Societe') {
1580 $tva_tx = get_default_tva($mysoc, $object->thirdparty);
1581 } else {
1582 $tva_tx = 0;
1583 }
1584 } elseif ((float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') > 0) {
1585 $tva_tx = (float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY');
1586 } else {
1587 $tva_tx = 0;
1588 }
1589
1590 // Pass the amount already signed: addline() forces -abs() on credit notes with the default setup, so this
1591 // changes nothing there, but it keeps the line negative when that forcing is relaxed (see addline()).
1592 $object->addline($langs->trans('invoiceAvoirLineWithPaymentRestAmount'), 0, 1, $tva_tx, 0, 0, 0, 0, '', '', 0, 0, 0, 'TTC', -$remain_to_pay);
1593 }
1594 }
1595
1596 // Add link between credit note and origin
1597 if (!empty($object->fk_facture_source) && $id > 0) {
1598 $facture_source->fetch($object->fk_facture_source);
1599 $facture_source->fetchObjectLinked();
1600
1601 if (!empty($facture_source->linkedObjectsIds)) {
1602 foreach ($facture_source->linkedObjectsIds as $sourcetype => $TIds) {
1603 $object->add_object_linked($sourcetype, current($TIds));
1604 }
1605 }
1606 }
1607 }
1608 }
1609
1610 // Standard invoice or Deposit invoice, created from a Predefined template invoice
1611 if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT) && GETPOSTINT('fac_rec') > 0) {
1612 if (empty($dateinvoice)) {
1613 $error++;
1614 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1615 $action = 'create';
1616 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1617 $error++;
1618 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1619 $action = 'create';
1620 }
1621
1622
1623 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1624 $error++;
1625 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1626 $action = 'create';
1627 }
1628
1629 if (!$error) {
1630 $object->socid = GETPOSTINT('socid');
1631 $object->type = GETPOSTINT('type');
1632 $object->subtype = GETPOSTINT('subtype');
1633 $object->ref = GETPOST('ref');
1634 $object->date = $dateinvoice;
1635 $object->date_pointoftax = $date_pointoftax;
1636 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1637 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1638
1639 $object->ref_customer = GETPOST('ref_client');
1640
1641 $object->model_pdf = GETPOST('model');
1642 $object->fk_project = GETPOSTINT('projectid');
1643 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1644 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1645 $object->fk_account = GETPOSTINT('fk_account');
1646 $object->amount = price2num(GETPOST('amount'));
1647 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1648 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1649 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1650 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1651
1652 // Source facture
1653 $object->fac_rec = GETPOSTINT('fac_rec');
1654
1655 $id = $object->create($user); // This include recopy of links from recurring invoice and recurring invoice lines
1656 }
1657 }
1658
1659 // Standard or deposit invoice, not from a Predefined template invoice
1660 if ((GETPOST('type') == Facture::TYPE_STANDARD
1661 || GETPOST('type') == Facture::TYPE_DEPOSIT
1662 || GETPOST('type') == Facture::TYPE_PROFORMA
1663 || (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') <= 0))
1664 && GETPOST('fac_rec') <= 0) {
1665 $typeamount = GETPOST('typedeposit', 'aZ09');
1666 $valuestandardinvoice = price2num(str_replace('%', '', GETPOST('valuestandardinvoice', 'alpha')), 'MU');
1667 $valuedeposit = price2num(str_replace('%', '', GETPOST('valuedeposit', 'alpha')), 'MU');
1668
1669 if (GETPOSTINT('socid') < 1) {
1670 $error++;
1671 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Customer")), null, 'errors');
1672 $action = 'create';
1673 }
1674
1675 if (empty($dateinvoice)) {
1676 $error++;
1677 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1678 $action = 'create';
1679 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1680 $error++;
1681 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1682 $action = 'create';
1683 }
1684
1685
1686 if (GETPOST('type') == Facture::TYPE_STANDARD) {
1687 if ($valuestandardinvoice < 0 || $valuestandardinvoice > 100) {
1688 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1689 $error++;
1690 $action = 'create';
1691 }
1692 } elseif (GETPOST('type') == Facture::TYPE_DEPOSIT) {
1693 if ($typeamount && !empty($origin) && !empty($originid)) {
1694 if ($typeamount == 'amount' && $valuedeposit <= 0) {
1695 setEventMessages($langs->trans("ErrorAnAmountWithoutTaxIsRequired"), null, 'errors');
1696 $error++;
1697 $action = 'create';
1698 }
1699 if ($typeamount == 'variable' && $valuedeposit <= 0) {
1700 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1701 $error++;
1702 $action = 'create';
1703 }
1704 if ($typeamount == 'variablealllines' && $valuedeposit <= 0) {
1705 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1706 $error++;
1707 $action = 'create';
1708 }
1709 }
1710 }
1711
1712
1713 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1714 $error++;
1715 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1716 $action = 'create';
1717 }
1718
1719 if (!$error) {
1720 $object->socid = GETPOSTINT('socid');
1721 $object->type = GETPOSTINT('type');
1722 $object->subtype = GETPOSTINT('subtype');
1723 $object->ref = GETPOST('ref');
1724 $object->date = $dateinvoice;
1725 $object->date_pointoftax = $date_pointoftax;
1726 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1727 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1728 $object->ref_client = GETPOST('ref_client');
1729 $object->ref_customer = GETPOST('ref_client');
1730 $object->model_pdf = GETPOST('model');
1731 $object->fk_project = GETPOSTINT('projectid');
1732 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1733 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1734 $object->fk_account = GETPOSTINT('fk_account');
1735 $object->amount = price2num(GETPOST('amount'));
1736 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1737 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1738 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1739 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1740
1741 if (GETPOST('type') == Facture::TYPE_SITUATION) {
1742 $object->situation_counter = 1;
1743 $object->situation_final = 0;
1744 $object->situation_cycle_ref = $object->newCycle();
1745 }
1746
1747 if (in_array($object->type, $retainedWarrantyInvoiceAvailableType)) {
1748 $object->retained_warranty = GETPOSTINT('retained_warranty');
1749 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
1750 } else {
1751 $object->retained_warranty = 0;
1752 $object->retained_warranty_fk_cond_reglement = 0;
1753 }
1754
1755 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
1756 if (!empty($retained_warranty_date_limit) && dol_stringtotime($retained_warranty_date_limit)) {
1757 $object->retained_warranty_date_limit = dol_stringtotime($retained_warranty_date_limit);
1758 }
1759 $object->retained_warranty_date_limit = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : $object->calculate_date_lim_reglement($object->retained_warranty_fk_cond_reglement);
1760
1761 $object->fetch_thirdparty();
1762
1763 // If creation from another object of another module (Example: origin=propal, originid=1)
1764 if (!empty($origin) && !empty($originid)) {
1765 $regs = array();
1766 // Parse element/subelement (ex: project_task)
1767 $element = $subelement = $origin;
1768 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
1769 $element = $regs[1];
1770 $subelement = $regs[2];
1771 }
1772
1773 // For compatibility
1774 if ($element == 'order') {
1775 $element = $subelement = 'commande';
1776 }
1777 if ($element == 'propal') {
1778 $element = 'comm/propal';
1779 $subelement = 'propal';
1780 }
1781 if ($element == 'contract') {
1782 $element = $subelement = 'contrat';
1783 }
1784 if ($element == 'inter') {
1785 $element = $subelement = 'fichinter';
1786 }
1787 if ($element == 'shipping') {
1788 $element = $subelement = 'expedition';
1789 }
1790
1791 $object->origin = $origin; // deprecated
1792 $object->origin_type = $origin;
1793 $object->origin_id = $originid;
1794
1795 // Possibility to add external linked objects with hooks
1796 $object->linked_objects[$object->origin_type] = $object->origin_id;
1797 // link with order if it is a shipping invoice
1798 if ($object->origin == 'shipping') {
1799 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1800 $exp = new Expedition($db);
1801 $exp->fetch($object->origin_id);
1802 $exp->fetchObjectLinked();
1803 if (is_array($exp->linkedObjectsIds['commande']) && count($exp->linkedObjectsIds['commande']) > 0) {
1804 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1805 $object->linked_objects['commande'] = $value;
1806 }
1807 }
1808 }
1809
1810 if (GETPOSTISARRAY('other_linked_objects')) {
1811 $object->linked_objects = array_merge($object->linked_objects, GETPOST('other_linked_objects', 'array:int'));
1812 }
1813
1814 $id = $object->create($user); // This include class to add_object_linked() and add add_contact()
1815
1816 if ($id > 0) {
1817 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
1818
1819 $classname = ucfirst($subelement);
1820 $srcobject = new $classname($db);
1821 '@phan-var-force CommonObject $srcobject';
1822
1823 dol_syslog("Try to find source object origin_type=".$object->origin_type." origin_id=".$object->origin_id." to add lines or deposit lines");
1824 $result = $srcobject->fetch($object->origin_id);
1825
1826 $i = -1; // Ensure initialised for static analysis, but with invalid idx.
1827 // If deposit invoice - down payment with 1 line (fixed amount or percent)
1828 if (GETPOST('type') == Facture::TYPE_DEPOSIT && in_array($typeamount, array('amount', 'variable'))) {
1829 // Define the array $amountdeposit
1830 $amountdeposit = array();
1831 $lines = array();
1832 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA')) { // We want to split the discount line into several lines, one per vat rate.
1833 if ($typeamount == 'amount') {
1834 $amount = (float) $valuedeposit;
1835 } else {
1836 $amount = $srcobject->total_ttc * ((float) $valuedeposit / 100);
1837 }
1838
1839 $TTotalByTva = array();
1840 foreach ($srcobject->lines as &$line) {
1841 if (empty($line->qty)) {
1842 continue; // We discard qty=0, it is an option
1843 }
1844 if (!empty($line->special_code)) {
1845 continue;
1846 }
1847 $TTotalByTva[$line->tva_tx] += $line->total_ttc;
1848 }
1849 '@phan-var-force array<string,float> $TTotalByTva';
1850
1851 $amount_ttc_diff = 0.;
1852 foreach ($TTotalByTva as $tva => &$total) {
1853 if (empty($amountdeposit[$tva])) {
1854 $amountdeposit[$tva] = 0;
1855 }
1856 $coef = $total / $srcobject->total_ttc; // Calc coef
1857 $am = $amount * $coef;
1858 $amount_ttc_diff += $am;
1859 $amountdeposit[$tva] += $am / (1 + (float) $tva / 100); // Convert into HT for the addline
1860 }
1861 } else {
1862 if ($typeamount == 'amount') {
1863 $amountdeposit[0] = $valuedeposit;
1864 } elseif ($typeamount == 'variable') {
1865 if ($result > 0) {
1866 $totalamount = 0;
1867 $lines = $srcobject->lines;
1868 $numlines = count($lines);
1869 for ($i = 0; $i < $numlines; $i++) {
1870 $qualified = 1;
1871 if (empty($lines[$i]->qty)) {
1872 $qualified = 0; // We discard qty=0, it is an option
1873 }
1874 if (!empty($lines[$i]->special_code)) {
1875 $qualified = 0; // We discard special_code (frais port, ecotaxe, option, ...)
1876 }
1877 if ($qualified) {
1878 $totalamount += $lines[$i]->total_ht; // Fixme : is it not for the customer ? Shouldn't we take total_ttc ?
1879 $tva_tx = $lines[$i]->tva_tx;
1880
1881 if (empty($amountdeposit[$tva_tx])) {
1882 $amountdeposit[$tva_tx] = 0;
1883 }
1884 $amountdeposit[$tva_tx] += ($lines[$i]->total_ht * (float) $valuedeposit) / 100;
1885 }
1886 }
1887
1888 if ($totalamount == 0) {
1889 $amountdeposit[0] = 0;
1890 }
1891 } else {
1892 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
1893 $error++;
1894 }
1895 }
1896
1897 $amount_ttc_diff = $amountdeposit[0];
1898 }
1899
1900 foreach ($amountdeposit as $tva => $amount) {
1901 if (empty($amount)) {
1902 continue;
1903 }
1904
1905 $arraylist = array(
1906 'amount' => 'FixAmount',
1907 'variable' => 'VarAmount'
1908 );
1909 $descline = '(DEPOSIT)';
1910 //$descline.= ' - '.$langs->trans($arraylist[$typeamount]);
1911 if ($typeamount == 'amount') {
1912 $descline .= ' ('.price($valuedeposit, 0, $langs, 0, - 1, - 1, (!empty($object->multicurrency_code) ? $object->multicurrency_code : $conf->currency)).')';
1913 } elseif ($typeamount == 'variable') {
1914 $descline .= ' ('.$valuedeposit.'%)';
1915 }
1916
1917 $descline .= ' - '.$srcobject->ref;
1918 $result = $object->addline(
1919 $descline,
1920 (float) $amount, // subprice
1921 1, // quantity
1922 $tva, // vat rate
1923 0, // localtax1_tx
1924 0, // localtax2_tx
1925 getDolGlobalInt('INVOICE_PRODUCTID_DEPOSIT'), // fk_product
1926 0, // remise_percent
1927 0, // date_start
1928 0, // date_end
1929 0,
1930 $i >= 0 ? $lines[$i]->info_bits : 0, // info_bits
1931 0,
1932 'HT',
1933 0,
1934 0, // product_type
1935 1,
1936 $i >= 0 ? $lines[$i]->special_code : 0,
1937 $object->origin_type,
1938 0,
1939 0,
1940 0,
1941 0,
1942 '',
1943 array(), // array_options
1944 100,
1945 0,
1946 null,
1947 0,
1948 '',
1949 (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA') ? 0 : 1)
1950 );
1951 }
1952
1953 $diff = $object->total_ttc - $amount_ttc_diff;
1954
1955 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA') && $diff != 0) {
1956 $object->fetch_lines();
1957 $subprice_diff = $object->lines[0]->subprice - $diff / (1 + $object->lines[0]->tva_tx / 100);
1958 $object->updateline($object->lines[0]->id, $object->lines[0]->desc, $subprice_diff, $object->lines[0]->qty, $object->lines[0]->remise_percent, $object->lines[0]->date_start, $object->lines[0]->date_end, $object->lines[0]->tva_tx, 0, 0, 'HT', $object->lines[0]->info_bits, $object->lines[0]->product_type, 0, 0, 0, $object->lines[0]->pa_ht, $object->lines[0]->label, 0, array(), 100);
1959 }
1960 }
1961
1962 // standard invoice, credit note, or down payment from a percent of all lines
1963 if (GETPOST('type') != Facture::TYPE_DEPOSIT || (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines')) {
1964 $lines = array();
1965
1966 if ($result > 0) {
1967 $lines = $srcobject->lines;
1968 if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
1969 $srcobject->fetch_lines();
1970 $lines = $srcobject->lines;
1971 }
1972
1973 // If we create a standard invoice with a percent, we change amount by changing the qty
1974 if (GETPOST('type') == Facture::TYPE_STANDARD && $valuestandardinvoice > 0 && $valuestandardinvoice < 100) {
1975 if (is_array($lines)) {
1976 foreach ($lines as $line) {
1977 // We keep ->subprice and ->pa_ht, but we change the qty
1978 $line->qty = (float) price2num((float) $line->qty * (float) $valuestandardinvoice / 100, 'MS');
1979 }
1980 }
1981 }
1982 // If we create a down payment with a percent on all lines, we change amount by changing the qty
1983 if (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines') {
1984 if (is_array($lines)) {
1985 foreach ($lines as $line) {
1986 // We keep ->subprice and ->pa_ht, but we change the qty
1987 $line->qty = (float) price2num((float) $line->qty * (float) $valuedeposit / 100, 'MS');
1988 }
1989 }
1990 }
1991
1992 $fk_parent_line = 0;
1993 $num = count($lines);
1994
1995 for ($i = 0; $i < $num; $i++) {
1996 if (!in_array($lines[$i]->id, $selectedLines)) {
1997 continue; // Skip unselected lines
1998 }
1999
2000 // Don't add lines with qty 0 when coming from a shipment including all order lines
2001 if ($srcobject->element == 'shipping' && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS') && $lines[$i]->qty == 0) {
2002 continue;
2003 }
2004 // Don't add closed lines when coming from a contract (Set constant to '0,5' to exclude also inactive lines)
2005 if (!isset($conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE)) {
2006 $conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE = '5';
2007 }
2008 if ($srcobject->element == 'contrat' && in_array($lines[$i]->statut, explode(',', getDolGlobalString('CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE')))) {
2009 continue;
2010 }
2011
2012 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
2013 $desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : '');
2014
2015 if ($object->situation_counter == 1) {
2016 $lines[$i]->situation_percent = 0;
2017 }
2018
2019 if ($lines[$i]->subprice < 0 && !getDolGlobalString('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN')) {
2020 // Negative line, we create a discount line
2021 if (empty($desc)) {
2022 $desc = $label ? $label : $langs->trans('Discount');
2023 }
2024
2025 $discount = new DiscountAbsolute($db);
2026 $discount->fk_soc = $object->socid;
2027 $discount->socid = $object->socid;
2028 $discount->amount_ht = abs($lines[$i]->total_ht);
2029 $discount->amount_tva = abs($lines[$i]->total_tva);
2030 $discount->amount_ttc = abs($lines[$i]->total_ttc);
2031 $discount->total_ht = abs($lines[$i]->total_ht);
2032 $discount->total_tva = abs($lines[$i]->total_tva);
2033 $discount->total_ttc = abs($lines[$i]->total_ttc);
2034 $discount->tva_tx = $lines[$i]->tva_tx;
2035 $discount->localtax1_tx = $lines[$i]->localtax1_tx;
2036 $discount->localtax2_tx = $lines[$i]->localtax2_tx;
2037 $discount->localtax1_type = $lines[$i]->localtax1_type;
2038 $discount->localtax2_type = $lines[$i]->localtax2_type;
2039 $discount->total_localtax1 = abs($lines[$i]->total_localtax1);
2040 $discount->total_localtax2 = abs($lines[$i]->total_localtax2);
2041 $discount->fk_user = $user->id;
2042 $discount->description = $desc;
2043 $discount->multicurrency_subprice = abs($lines[$i]->multicurrency_subprice);
2044 $discount->multicurrency_amount_ht = abs($lines[$i]->multicurrency_total_ht);
2045 $discount->multicurrency_amount_tva = abs($lines[$i]->multicurrency_total_tva);
2046 $discount->multicurrency_amount_ttc = abs($lines[$i]->multicurrency_total_ttc);
2047 $discount->multicurrency_total_ht = abs($lines[$i]->multicurrency_total_ht);
2048 $discount->multicurrency_total_tva = abs($lines[$i]->multicurrency_total_tva);
2049 $discount->multicurrency_total_ttc = abs($lines[$i]->multicurrency_total_ttc);
2050
2051 $discountid = $discount->create($user);
2052 if ($discountid > 0) {
2053 $result = $object->insert_discount($discountid); // This include link_to_invoice
2054 } else {
2055 setEventMessages($discount->error, $discount->errors, 'errors');
2056 $error++;
2057 break;
2058 }
2059 } else {
2060 // Positive line
2061 // we keep first type from product if exist, otherwise we keep type from line (free line) and at last default Product
2062 $product_type = $lines[$i]->product_type ?? ($lines[$i]->type ?? Product::TYPE_PRODUCT);
2063
2064 // Date start
2065 $date_start = false;
2066 if (isset($lines[$i]->date_debut_prevue)) {
2067 $date_start = $lines[$i]->date_debut_prevue;
2068 }
2069 if (isset($lines[$i]->date_debut_reel)) {
2070 $date_start = $lines[$i]->date_debut_reel;
2071 }
2072 if (isset($lines[$i]->date_start)) {
2073 $date_start = $lines[$i]->date_start;
2074 }
2075
2076 // Date end
2077 $date_end = false;
2078 if (isset($lines[$i]->date_fin_prevue)) {
2079 $date_end = $lines[$i]->date_fin_prevue;
2080 }
2081 if (isset($lines[$i]->date_fin_reel)) {
2082 $date_end = $lines[$i]->date_fin_reel;
2083 }
2084 if (isset($lines[$i]->date_end)) {
2085 $date_end = $lines[$i]->date_end;
2086 }
2087
2088 // Reset fk_parent_line for no child products and special product
2089 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
2090 $fk_parent_line = 0;
2091 }
2092
2093 $array_options = array();
2094 // Extrafields
2095 if (method_exists($lines[$i], 'fetch_optionals')) {
2096 $lines[$i]->fetch_optionals();
2097 $array_options = $lines[$i]->array_options;
2098 }
2099
2100 $tva_tx = $lines[$i]->tva_tx;
2101 if (!empty($lines[$i]->vat_src_code) && !preg_match('/\‍(/', (string) $tva_tx)) {
2102 $tva_tx .= ' ('.$lines[$i]->vat_src_code.')';
2103 }
2104
2105 // View third's localtaxes for NOW and do not use value from origin.
2106 // TODO Is this really what we want ? Yes if source is template invoice but what if proposal or order ?
2107 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty);
2108 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty);
2109
2110 // Preserve the original entry mode of the line so the total is computed from the typed value (no rounding drift).
2111 $line_price_base_type = $lines[$i]->getPriceBaseType();
2112 $result = $object->addline(
2113 $desc,
2114 $lines[$i]->subprice,
2115 $lines[$i]->qty,
2116 $tva_tx,
2117 $localtax1_tx,
2118 $localtax2_tx,
2119 $lines[$i]->fk_product,
2120 $lines[$i]->remise_percent,
2122 $date_end,
2123 0,
2124 (int) $lines[$i]->info_bits,
2125 isset($lines[$i]->fk_remise_except) ? $lines[$i]->fk_remise_except : null,
2126 $line_price_base_type,
2127 (float) $lines[$i]->subprice_ttc,
2128 $product_type,
2129 $lines[$i]->rang,
2130 $lines[$i]->special_code,
2131 $object->origin_type,
2132 $lines[$i]->rowid,
2133 $fk_parent_line,
2134 isset($lines[$i]->fk_fournprice) ? $lines[$i]->fk_fournprice : null,
2135 $lines[$i]->pa_ht,
2136 $label,
2137 $array_options,
2138 $lines[$i]->situation_percent ?? 100,
2139 $lines[$i]->fk_prev_id ?? 0,
2140 $lines[$i]->fk_unit,
2141 0,
2142 '',
2143 0
2144 );
2145
2146 if ($result > 0) {
2147 foreach ($object->lines as $line) {
2148 if ($line->id == $result) {
2149 $line->extraparams = $lines[$i]->extraparams;
2150 $line->setExtraParameters();
2151 }
2152 }
2153
2154 $lineid = $result;
2155 } else {
2156 $lineid = 0;
2157 $error++;
2158 break;
2159 }
2160
2161 // Defined the new fk_parent_line
2162 if ($result > 0 && $lines[$i]->product_type == 9) {
2163 $fk_parent_line = $result;
2164 }
2165 }
2166 }
2167 } else {
2168 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
2169 $error++;
2170 }
2171 }
2172
2173 $object->update_price(1, 'auto', 0, $mysoc);
2174
2175 $object->line_order(true, 'DESC');
2176
2177 // Now we create same links to contact than the ones found on origin object
2178 /* Useless, already into the create
2179 if (getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN')) {
2180 $originforcontact = $object->origin;
2181 $originidforcontact = $object->origin_id;
2182 if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
2183 {
2184 $originforcontact=$srcobject->origin;
2185 $originidforcontact=$srcobject->origin_id;
2186 }
2187 $sqlcontact = "SELECT code, fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
2188 $sqlcontact.= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$db->escape($originforcontact)."'";
2189
2190 $resqlcontact = $db->query($sqlcontact);
2191 if ($resqlcontact)
2192 {
2193 while($objcontact = $db->fetch_object($resqlcontact))
2194 {
2195 //print $objcontact->code.'-'.$objcontact->fk_socpeople."\n";
2196 $object->add_contact($objcontact->fk_socpeople, $objcontact->code);
2197 }
2198 }
2199 else dol_print_error($resqlcontact);
2200 }*/
2201
2202 // Hooks
2203 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id, 'objFrom' => $srcobject);
2204 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
2205 // modified by hook
2206 if ($reshook < 0) {
2207 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2208 $error++;
2209 }
2210 } else {
2211 setEventMessages($object->error, $object->errors, 'errors');
2212 $error++;
2213 }
2214 } else { // If some invoice's lines coming from page
2215 $id = $object->create($user);
2216
2217 for ($i = 1; $i <= $NBLINES; $i++) {
2218 if (GETPOSTINT('idprod'.$i)) {
2219 $product = new Product($db);
2220 $product->fetch(GETPOSTINT('idprod'.$i));
2221 $startday = dol_mktime(12, 0, 0, GETPOSTINT('date_start'.$i.'month'), GETPOSTINT('date_start'.$i.'day'), GETPOSTINT('date_start'.$i.'year'));
2222 $endday = dol_mktime(12, 0, 0, GETPOSTINT('date_end'.$i.'month'), GETPOSTINT('date_end'.$i.'day'), GETPOSTINT('date_end'.$i.'year'));
2223 $result = $object->addline($product->description, $product->price, (float) price2num(GETPOST('qty'.$i), 'MS'), $product->tva_tx, $product->localtax1_tx, $product->localtax2_tx, GETPOSTINT('idprod'.$i), (float) price2num(GETPOST('remise_percent'.$i), '', 2), $startday, $endday, 0, 0, 0, $product->price_base_type, $product->price_ttc, $product->type, -1, 0, '', 0, 0, 0, 0, '', array(), 100, 0, $product->fk_unit, 0, '', 1);
2224 }
2225 }
2226
2227 $object->update_price(1, 'auto', 0, $mysoc);
2228 }
2229 }
2230 }
2231
2232 // Situation invoices
2233 if (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') > 0) {
2234 if (empty($dateinvoice)) {
2235 $error++;
2236 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date"));
2237 setEventMessages($mesg, null, 'errors');
2238 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
2239 $error++;
2240 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
2241 $action = 'create';
2242 }
2243
2244 if (!(GETPOSTINT('situations') > 0)) {
2245 $error++;
2246 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSituation"));
2247 setEventMessages($mesg, null, 'errors');
2248 $action = 'create';
2249 }
2250
2251 if (!$error) {
2252 $result = $object->fetch(GETPOSTINT('situations'));
2253 $object->fk_facture_source = GETPOSTINT('situations');
2255
2256 if (!empty($origin) && !empty($originid)) {
2257 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2258
2259 $object->origin = $origin; // deprecated
2260 $object->origin_type = $origin;
2261 $object->origin_id = $originid;
2262
2263 // retained warranty
2264 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
2265 $retained_warranty = GETPOSTINT('retained_warranty');
2266 if (price2num($retained_warranty) > 0) {
2267 $object->retained_warranty = (float) price2num($retained_warranty);
2268 }
2269
2270 if (GETPOSTINT('retained_warranty_fk_cond_reglement') > 0) {
2271 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
2272 }
2273
2274 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
2275 if (!empty($retained_warranty_date_limit) && $db->jdate($retained_warranty_date_limit)) {
2276 $object->retained_warranty_date_limit = $db->jdate($retained_warranty_date_limit);
2277 }
2278 $object->retained_warranty_date_limit = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : $object->calculate_date_lim_reglement($object->retained_warranty_fk_cond_reglement);
2279 }
2280
2281 foreach ($object->lines as $i => &$line) {
2282 $line->fk_prev_id = $line->id;
2283 $line->fetch_optionals();
2284 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
2285 $line->situation_percent = 0; // New situation percent must be 0 (No cumulative)
2286 } else {
2287 $line->situation_percent = $line->get_prev_progress($object->id); // get good progress including credit note
2288 }
2289
2290 // The $line->situation_percent has been modified, so we must recalculate all amounts
2291 $tabprice = calcul_price_total($line->qty, $line->subprice, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 0, 'HT', 0, $line->product_type, $mysoc, array(), $line->situation_percent);
2292 $line->total_ht = (float) $tabprice[0];
2293 $line->total_tva = (float) $tabprice[1];
2294 $line->total_ttc = (float) $tabprice[2];
2295 $line->total_localtax1 = (float) $tabprice[9];
2296 $line->total_localtax2 = (float) $tabprice[10];
2297 $line->multicurrency_total_ht = (float) $tabprice[16];
2298 $line->multicurrency_total_tva = (float) $tabprice[17];
2299 $line->multicurrency_total_ttc = (float) $tabprice[18];
2300
2301 // If fk_remise_except defined we check if the reduction has already been applied
2302 if ($line->fk_remise_except) {
2303 $discount = new DiscountAbsolute($line->db);
2304 $result = $discount->fetch((int) $line->fk_remise_except);
2305 if ($result > 0) {
2306 // Check if discount not already affected to another invoice
2307 if ($discount->fk_facture_line > 0) {
2308 $line->fk_remise_except = 0;
2309 }
2310 }
2311 }
2312 }
2313 }
2314
2315 $object->fetch_thirdparty();
2316 $object->date = $dateinvoice;
2317 $object->date_pointoftax = $date_pointoftax;
2318 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
2319 $object->note = trim(GETPOST('note', 'restricthtml'));
2320 $object->note_private = trim(GETPOST('note', 'restricthtml'));
2321 $object->ref_client = GETPOST('ref_client', 'alpha');
2322 $object->ref_customer = GETPOST('ref_client', 'alpha');
2323 $object->model_pdf = GETPOST('model', 'alpha');
2324 $object->fk_project = GETPOSTINT('projectid');
2325 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
2326 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
2327 $object->fk_account = GETPOSTINT('fk_account');
2328
2329
2330 // Special properties of replacement invoice
2331
2332 $object->situation_counter += 1;
2333
2334 // Set extrafields from the create form BEFORE createFromCurrent(), so they are already
2335 // present when create() inserts them and fires the BILL_CREATE trigger. This avoids firing
2336 // BILL_CREATE a second time just to expose the extrafields (#32217).
2337 $extrafields->fetch_name_optionals_label($object->table_element);
2338 $extrafields->setOptionalsFromPost(null, $object);
2339
2340 $id = $object->createFromCurrent($user);
2341 if ($id <= 0) {
2342 $mesg = $object->error;
2343 } else {
2344 $nextSituationInvoice = new Facture($db);
2345 $nextSituationInvoice->fetch($id);
2346
2347 // Hooks
2348 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id);
2349 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $nextSituationInvoice, $action); // Note that $action and $object may have been
2350 // modified by hook
2351 if ($reshook < 0) {
2352 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2353 $error++;
2354 }
2355 }
2356 }
2357 }
2358
2359 // End of object creation, we show it
2360 if ($id > 0 && !$error) {
2361 if (isModEnabled('category')) {
2362 $categories = GETPOST('categories', 'array');
2363 if (method_exists($object, 'setCategories')) {
2364 $object->setCategories($categories);
2365 }
2366 }
2367
2368 $db->commit();
2369
2370 // Define output language
2371 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && count($object->lines)) {
2372 $outputlangs = $langs;
2373 $newlang = '';
2374 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
2375 $newlang = GETPOST('lang_id', 'aZ09');
2376 }
2377 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2378 if (empty($object->thirdparty)) {
2379 $object->fetch_thirdparty();
2380 }
2381 $newlang = $object->thirdparty->default_lang;
2382 }
2383 if (!empty($newlang)) {
2384 $outputlangs = new Translate("", $conf);
2385 $outputlangs->setDefaultLang($newlang);
2386 $outputlangs->load('products');
2387 }
2388 $model = $object->model_pdf;
2389 $ret = $object->fetch($id); // Reload to get new records
2390
2391 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2392 if ($result < 0) {
2393 setEventMessages($object->error, $object->errors, 'errors');
2394 }
2395 }
2396
2397 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2398 exit();
2399 } else {
2400 $db->rollback();
2401 $action = 'create';
2402 $_GET["origin"] = GETPOST("origin", 'alpha');
2403 $_GET["originid"] = GETPOSTINT("originid");
2404 setEventMessages($object->error, $object->errors, 'errors');
2405 }
2406 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
2407 // Define date start and date end for all line
2408 $alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
2409 $alldate_end = dol_mktime(GETPOSTINT('alldate_endhour'), GETPOSTINT('alldate_endmin'), 0, GETPOSTINT('alldate_endmonth'), GETPOSTINT('alldate_endday'), GETPOSTINT('alldate_endyear'));
2410 foreach ($object->lines as $line) {
2411 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2412 continue;
2413 }
2414 if ($line->product_type == 1) { // only service line
2415 // Preserve the original entry mode of the line so the total is not drifted by rounding.
2416 $line_price_base_type = $line->getPriceBaseType();
2417 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
2418 $result = $object->updateline($line->id, $line->desc, $line_pu, $line->qty, $line->remise_percent, $alldate_start, $alldate_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line_price_base_type, $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice);
2419 }
2420 }
2421 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
2422 // Define vat_rate
2423 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
2424 $vat_rate = str_replace('*', '', $vat_rate);
2425 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
2426 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
2427 foreach ($object->lines as $line) {
2428 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2429 continue;
2430 }
2431 // Preserve the original entry mode of the line so the total is not drifted by rounding.
2432 $line_price_base_type = $line->getPriceBaseType();
2433 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
2434 $result = $object->updateline($line->id, $line->desc, $line_pu, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $vat_rate, $localtax1_rate, $localtax2_rate, $line_price_base_type, $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice);
2435 }
2436 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
2437 // Define vat_rate
2438 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
2439 $remise_percent = str_replace('*', '', $remise_percent);
2440 foreach ($object->lines as $line) {
2441 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2442 continue;
2443 }
2444 $tvatx = $line->tva_tx;
2445 if (!empty($line->vat_src_code)) {
2446 $tvatx .= ' ('.$line->vat_src_code.')';
2447 }
2448 // Preserve the original entry mode of the line so the total is not drifted by rounding.
2449 $line_price_base_type = $line->getPriceBaseType();
2450 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
2451 $result = $object->updateline($line->id, $line->desc, $line_pu, $line->qty, (float) $remise_percent, $line->date_start, $line->date_end, $tvatx, $line->localtax1_tx, $line->localtax2_tx, $line_price_base_type, $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice);
2452 }
2453 } elseif ($action == 'confirm_addtextline' && $usercancreate) {
2454 // Handling adding a new text line for subtotals module
2455
2456 $langs->load('subtotals');
2457
2458 $desc = GETPOST('subtotaltextcontent', 'restricthtml');
2459
2460 // Insert line
2461 $result = $object->addSubtotalLine($langs, $desc, 0, array());
2462
2463 if ($result >= 0) {
2464 if ($result == 0) {
2465 setEventMessages($object->error, $object->errors, 'warnings');
2466 }
2467 $ret = $object->fetch($object->id); // Reload to get new records
2468 $object->fetch_thirdparty();
2469
2470 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2471 // Define output language
2472 $outputlangs = $langs;
2473 $newlang = GETPOST('lang_id', 'alpha');
2474 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2475 $newlang = $object->thirdparty->default_lang;
2476 }
2477 if (!empty($newlang)) {
2478 $outputlangs = new Translate("", $conf);
2479 $outputlangs->setDefaultLang($newlang);
2480 }
2481
2482 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2483 }
2484 } else {
2485 setEventMessages($object->error, $object->errors, 'errors');
2486 }
2487 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2488 exit();
2489 } elseif ($action == 'confirm_addtitleline' && $usercancreate) {
2490 // Handling adding a new title line for subtotals module
2491
2492 $langs->load('subtotals');
2493
2494 $desc = GETPOST('subtotallinedesc', 'alphanohtml');
2495 $depth = GETPOSTINT('subtotallinelevel') ?? 1;
2496
2497 $subtotal_options = array();
2498
2499 foreach (Facture::$TITLE_OPTIONS as $option) {
2500 $value = GETPOST($option, 'alphanohtml');
2501 if ($value) {
2502 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
2503 }
2504 }
2505
2506 // Insert line
2507 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
2508
2509 if ($result >= 0) {
2510 if ($result == 0) {
2511 setEventMessages($object->error, $object->errors, 'warnings');
2512 }
2513 $ret = $object->fetch($object->id); // Reload to get new records
2514 $object->fetch_thirdparty();
2515
2516 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2517 // Define output language
2518 $outputlangs = $langs;
2519 $newlang = GETPOST('lang_id', 'alpha');
2520 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2521 $newlang = $object->thirdparty->default_lang;
2522 }
2523 if (!empty($newlang)) {
2524 $outputlangs = new Translate("", $conf);
2525 $outputlangs->setDefaultLang($newlang);
2526 }
2527
2528 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2529 }
2530 } else {
2531 setEventMessages($object->error, $object->errors, 'errors');
2532 }
2533 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2534 exit();
2535 } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) {
2536 // Handling adding a new subtotal line for subtotals module
2537
2538 $langs->load('subtotals');
2539
2540 $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml');
2541 foreach ($object->lines as $line) {
2542 if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) {
2543 $desc = $line->desc;
2544 $depth = -$line->qty;
2545 }
2546 }
2547
2548 $subtotal_options = array();
2549
2550 foreach (Facture::$SUBTOTAL_OPTIONS as $option) {
2551 $value = GETPOST($option, 'alphanohtml');
2552 if ($value) {
2553 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
2554 }
2555 }
2556
2557 // Insert line
2558 if (isset($desc) && isset($depth)) {
2559 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
2560 } else {
2561 $result = -1;
2562 $object->errors[] = $langs->trans("CorrespondingTitleNotFound");
2563 }
2564
2565 if ($result >= 0) {
2566 $ret = $object->fetch($object->id); // Reload to get new records
2567 $object->fetch_thirdparty();
2568
2569 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2570 // Define output language
2571 $outputlangs = $langs;
2572 $newlang = GETPOST('lang_id', 'alpha');
2573 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2574 $newlang = $object->thirdparty->default_lang;
2575 }
2576 if (!empty($newlang)) {
2577 $outputlangs = new Translate("", $conf);
2578 $outputlangs->setDefaultLang($newlang);
2579 }
2580
2581 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2582 }
2583 } else {
2584 setEventMessages($object->error, $object->errors, 'errors');
2585 }
2586 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2587 exit();
2588 } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) {
2589 $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0;
2590 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva);
2591 } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) {
2592 $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0;
2593 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount);
2594 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && !GETPOST('submitforallmark', 'alpha') && $usercancreate) { // Add a new line
2595 $langs->load('errors');
2596 $error = 0;
2597
2598 // Set if we used free entry or predefined product
2599 $predef = '';
2600 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
2601
2602 $price_ht = '';
2603 $price_ht_devise = '';
2604 $price_ttc = '';
2605 $price_ttc_devise = '';
2606
2607 $price_min = '';
2608 $price_min_ttc = '';
2609
2610 if (GETPOST('price_ht') !== '') {
2611 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
2612 }
2613 if (GETPOST('multicurrency_price_ht') !== '') {
2614 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
2615 }
2616 if (GETPOST('price_ttc') !== '') {
2617 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
2618 }
2619 if (GETPOST('multicurrency_price_ttc') !== '') {
2620 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
2621 }
2622
2623 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
2624 if ($prod_entry_mode == 'free') {
2625 $idprod = 0;
2626 } else {
2627 $idprod = GETPOSTINT('idprod');
2628
2629 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
2630 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
2631 $error++;
2632 }
2633 }
2634
2635 $tva_tx = GETPOST('tva_tx', 'alpha');
2636
2637 $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
2638 $remise_percent = (GETPOSTISSET('remise_percent'.$predef) ? price2num(GETPOST('remise_percent'.$predef, 'alpha'), '', 2) : 0);
2639 if (empty($remise_percent)) {
2640 $remise_percent = 0;
2641 }
2642
2643 // Extrafields
2644 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
2645 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
2646 // Unset extrafield
2647 if (is_array($extralabelsline)) {
2648 // Get extra fields
2649 foreach ($extralabelsline as $key => $value) {
2650 unset($_POST["options_".$key.$predef]);
2651 }
2652 }
2653
2654 if ((empty($idprod) || $idprod < 0) && ($price_ht < 0) && ($qty < 0)) {
2655 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2656 $error++;
2657 }
2658 if (!$prod_entry_mode) {
2659 if (GETPOST('type') < 0 && !GETPOST('search_idprod')) {
2660 setEventMessages($langs->trans('ErrorChooseBetweenFreeEntryOrPredefinedProduct'), null, 'errors');
2661 $error++;
2662 }
2663 }
2664 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
2665 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
2666 $error++;
2667 }
2668
2669 // Do not allow negative lines for free products (invite to enter a discount instead)
2670 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0)
2671 && (((float) $price_ht < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht === '')
2672 && (((float) $price_ht_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht_devise === '')
2673 && ((float) $price_ttc < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES') || $price_ttc === '')
2674 && ((float) $price_ttc_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES') || $price_ttc_devise === '')
2675 && $object->type != $object::TYPE_CREDIT_NOTE) { // Unit price can be 0 but not ''
2676 if (((float) $price_ht < 0 || (float) $price_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
2677 $langs->load("errors");
2678 if ($object->type == $object::TYPE_DEPOSIT) {
2679 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
2680 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
2681 } else {
2682 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
2683 }
2684 $error++;
2685 }
2686 }
2687 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('price_ht') === '' && GETPOST('price_ttc') === '' && $price_ht_devise === '') { // Unit price can be 0 but not ''
2688 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('UnitPrice')), null, 'errors');
2689 $error++;
2690 }
2691
2692 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
2693 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
2694 $error++;
2695 }
2696 if ($qty == '') {
2697 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2698 $error++;
2699 }
2700 if ($qty < 0 && !getDolGlobalString('INVOICE_ENABLE_NEGATIVE_QTY')) {
2701 $langs->load("errors");
2702 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
2703 $error++;
2704 }
2705
2706 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
2707 if ($combinations = GETPOST('combinations', 'array:alphanohtml')) {
2708 //Check if there is a product with the given combination
2709 $prodcomb = new ProductCombination($db);
2710
2711 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
2712 $idprod = $res->fk_product_child;
2713 } else {
2714 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
2715 $error++;
2716 }
2717 }
2718 }
2719
2720 $price_base_type = null;
2721 if (!$error && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
2722 $ret = $object->fetch($id);
2723 if ($ret < 0) {
2724 dol_print_error($db, $object->error);
2725 exit();
2726 }
2727 $ret = $object->fetch_thirdparty();
2728
2729 // Clean parameters
2730 $date_start = dol_mktime(GETPOSTINT('date_start'.$predef.'hour'), GETPOSTINT('date_start'.$predef.'min'), GETPOSTINT('date_start'.$predef.'sec'), GETPOSTINT('date_start'.$predef.'month'), GETPOSTINT('date_start'.$predef.'day'), GETPOSTINT('date_start'.$predef.'year'));
2731 $date_end = dol_mktime(GETPOSTINT('date_end'.$predef.'hour'), GETPOSTINT('date_end'.$predef.'min'), GETPOSTINT('date_end'.$predef.'sec'), GETPOSTINT('date_end'.$predef.'month'), GETPOSTINT('date_end'.$predef.'day'), GETPOSTINT('date_end'.$predef.'year'));
2732 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
2733 $tva_npr = "";
2734
2735 // Define special_code for special lines
2736 $special_code = 0;
2737 // if (!GETPOST(qty)) $special_code=3; // Options should not exists on invoices
2738
2739 // Replaces $pu with that of the product
2740 // Replaces $desc with that of the product
2741 // Replaces $base_price_type with that of the product
2742 // Replaces $fk_unit with that of the product
2743 if (!empty($idprod) && $idprod > 0) {
2744 $prod = new Product($db);
2745 $prod->fetch($idprod);
2746
2747 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
2748
2749 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
2750 $pqp = (GETPOSTINT('pbq') ? GETPOSTINT('pbq') : 0);
2751
2752 $datapriceofproduct = $prod->getSellPrice($mysoc, $object->thirdparty, $pqp);
2753
2754 $pu_ht = $datapriceofproduct['pu_ht'];
2755 $pu_ttc = $datapriceofproduct['pu_ttc'];
2756 $price_min = $datapriceofproduct['price_min'];
2757 $price_min_ttc = (isset($datapriceofproduct['price_min_ttc'])) ? $datapriceofproduct['price_min_ttc'] : null;
2758 $price_base_type = empty($datapriceofproduct['price_base_type']) ? 'HT' : $datapriceofproduct['price_base_type'];
2759
2760 //$tva_tx = $datapriceofproduct['tva_tx'];
2761 //$tva_npr = $datapriceofproduct['tva_npr'];
2762 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
2763 $tmpprodvat = price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
2764
2765 // Set unit price to use.
2766 // Force price_base_type to match what the user actually entered, so the total is computed
2767 // from the typed value and not from the converted/rounded value (avoids 0.01 rounding drift).
2768 if (!empty($price_ht) || $price_ht === '0') {
2769 $pu_ht = price2num($price_ht, 'MU');
2770 $pu_ttc = price2num((float) $pu_ht * (1 + ($tmpvat / 100)), 'MU');
2771 $price_base_type = 'HT';
2772 } elseif (!empty($price_ht_devise) || $price_ht_devise === '0') {
2773 $pu_ht_devise = price2num($price_ht_devise, 'MU');
2774 $pu_ttc_devise = (float) price2num((float) $pu_ht_devise * (1 + ((float) $tmpvat / 100)), 'MU');
2775 $pu_ht = '';
2776 $pu_ttc = '';
2777 $price_base_type = 'HT';
2778 } elseif (!empty($price_ttc) || $price_ttc === '0') {
2779 $pu_ttc = price2num($price_ttc, 'MU');
2780 $pu_ht = price2num((float) $pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2781 $price_base_type = 'TTC';
2782 } elseif (!empty($price_ttc_devise) || (string) $price_ttc_devise === '0') {
2783 $pu_ttc_devise = (float) price2num($price_ttc_devise, 'MU');
2784 $pu_ht_devise = (float) price2num((float) $pu_ttc_devise / (1 + ((float) $tmpvat / 100)), 'MU');
2785 $pu_ht = '';
2786 $pu_ttc = '';
2787 $price_base_type = 'TTC';
2788 } elseif ($tmpvat != $tmpprodvat) {
2789 // Is this still used ?
2790 if ($price_base_type != 'HT') {
2791 $pu_ht = price2num($pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2792 } else {
2793 $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU');
2794 }
2795 }
2796
2797 $outputlangs = $langs;
2798 $newlang = '';
2799 $desc = '';
2800
2801 // Define output language
2802 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2803 if (/* empty($newlang) && */ GETPOST('lang_id', 'aZ09')) {
2804 $newlang = GETPOST('lang_id', 'aZ09');
2805 }
2806 if (empty($newlang)) {
2807 $newlang = $object->thirdparty->default_lang;
2808 }
2809 if (!empty($newlang)) {
2810 $outputlangs = new Translate("", $conf);
2811 $outputlangs->setDefaultLang($newlang);
2812 $outputlangs->load('products');
2813 }
2814
2815 $desc = (!empty($prod->multilangs [$outputlangs->defaultlang] ["description"])) ? $prod->multilangs [$outputlangs->defaultlang] ["description"] : $prod->description;
2816 } else {
2817 $desc = $prod->description;
2818 }
2819
2820 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
2821 // 'DoNotAutofillButAutoConcat'
2822 $desc = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
2823 } else {
2824 //'AutoFillFormFieldBeforeSubmit' or 'DoNotUseDescriptionOfProdut' => User has already done the modification they want
2825 $desc = $line_desc;
2826 }
2827
2828 // Add custom code and origin country into description
2829 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
2830 $tmptxt = '(';
2831 // Define output language
2832 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2833 if (!empty($prod->customcode)) {
2834 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
2835 }
2836 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2837 $tmptxt .= ' - ';
2838 }
2839 if (!empty($prod->country_code)) {
2840 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $outputlangs, 0);
2841 }
2842 } else {
2843 if (!empty($prod->customcode)) {
2844 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
2845 }
2846 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2847 $tmptxt .= ' - ';
2848 }
2849 if (!empty($prod->country_code)) {
2850 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $langs, 0);
2851 }
2852 }
2853 $tmptxt .= ')';
2854 $desc = dol_concatdesc($desc, $tmptxt);
2855 }
2856
2857 $type = $prod->type;
2858 $fk_unit = $prod->fk_unit;
2859 } else {
2860 if (!empty($price_ht)) {
2861 $pu_ht = price2num($price_ht, 'MU');
2862 } else {
2863 $pu_ht = '';
2864 }
2865 if (!empty($price_ttc)) {
2866 $pu_ttc = price2num($price_ttc, 'MU');
2867 } else {
2868 $pu_ttc = '';
2869 }
2870 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
2871 $tva_tx = str_replace('*', '', $tva_tx);
2872 if (empty($tva_tx)) {
2873 $tva_npr = 0;
2874 }
2875 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2876 $desc = $line_desc;
2877 $type = GETPOST('type');
2878 $fk_unit = GETPOST('units', 'alpha');
2879
2880 if ($pu_ttc && !$pu_ht) {
2881 $price_base_type = 'TTC';
2882 }
2883 }
2884
2885 // Define info_bits
2886 $info_bits = 0;
2887 if ($tva_npr) {
2888 $info_bits |= 0x01;
2889 }
2890
2891 // Local Taxes
2892 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
2893 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
2894
2895 $pu_ht_devise = price2num($price_ht_devise, '', 2);
2896 $pu_ttc_devise = price2num($price_ttc_devise, '', 2);
2897
2898 // Prepare a price equivalent for minimum price check
2899 $pu_equivalent = $pu_ht;
2900 $pu_equivalent_ttc = $pu_ttc;
2901
2902 $currency_tx = $object->multicurrency_tx;
2903
2904 // Check if we have a foreign currency
2905 // If so, we update the pu_equiv as the equivalent price in base currency
2906 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2907 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
2908 }
2909 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2910 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
2911 }
2912
2913 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
2914 /*
2915 if ($pu_equivalent) {
2916 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
2917 $pu_equivalent_ttc = ...
2918 } else {
2919 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
2920 $pu_equivalent_ht = ...
2921 }
2922 */
2923
2924 // Margin
2925 $fournprice = (int) (GETPOST('fournprice'.$predef) ? GETPOST('fournprice'.$predef) : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
2926 $buyingprice = price2num(GETPOST('buying_price'.$predef) != '' ? GETPOST('buying_price'.$predef) : ''); // If buying_price is '0', we must keep this value
2927
2928
2929 $price2num_pu_ht = price2num($pu_ht);
2930 $price2num_remise_percent = price2num($remise_percent);
2931 $price2num_price_min = price2num($price_min);
2932 $price2num_price_min_ttc = price2num($price_min_ttc);
2933 if (empty($price2num_pu_ht)) {
2934 $price2num_pu_ht = 0;
2935 }
2936 if (empty($price2num_remise_percent)) {
2937 $price2num_remise_percent = 0;
2938 }
2939 if (empty($price2num_price_min)) {
2940 $price2num_price_min = 0;
2941 }
2942 if (empty($price2num_price_min_ttc)) {
2943 $price2num_price_min_ttc = 0;
2944 }
2945
2946 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
2947 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
2948 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2949 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2950 setEventMessages($mesg, null, 'errors');
2951 $error++;
2952 } elseif ($pu_equivalent_ttc && $price_min_ttc && (((float) price2num($pu_equivalent_ttc) * (1 - $remise_percent / 100)) < (float) price2num($price_min_ttc)) && $price_base_type == 'TTC') {
2953 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2954 setEventMessages($mesg, null, 'errors');
2955 $error++;
2956 }
2957 }
2958
2959 if (!$error) {
2960 /* Disable strange code that use $lines[$i] that is not defined
2961 '@phan-var-force CommonObjectLine[] $lines';
2962 // Add batchinfo if the detail_batch array is defined
2963 if (isModEnabled('productbatch') && !empty($lines[$i]->detail_batch) && is_array($lines[$i]->detail_batch) && getDolGlobalString('INVOICE_INCUDE_DETAILS_OF_LOTS_SERIALS')) {
2964 $langs->load('productbatch');
2965 foreach ($lines[$i]->detail_batch as $batchline) {
2966 $desc .= ' '.$langs->trans('Batch').' '.$batchline->batch.' '.$langs->trans('printQty', $batchline->qty).' ';
2967 }
2968 }
2969 */
2970
2971 // Insert line
2972 $situation_percent = (GETPOSTISSET('progress') ? GETPOSTINT('progress') : 100);
2973
2974 $result = $object->addline($desc, $pu_ht, (float) $qty, $tva_tx, $localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $date_start, $date_end, 0, $info_bits, 0, $price_base_type, $pu_ttc, $type, min($rank, count($object->lines) + 1), $special_code, '', 0, GETPOSTINT('fk_parent_line'), (int) $fournprice, $buyingprice, $label, $array_options, $situation_percent, 0, $fk_unit, (float) $pu_ht_devise);
2975
2976 if ($result > 0) {
2977 $ret = $object->fetch($id); // Reload to get new records
2978 // Define output language and generate document
2979 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2980 $outputlangs = $langs;
2981 $newlang = '';
2982 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
2983 $newlang = GETPOST('lang_id', 'aZ09');
2984 }
2985 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2986 $newlang = $object->thirdparty->default_lang;
2987 }
2988 if (!empty($newlang)) {
2989 $outputlangs = new Translate("", $conf);
2990 $outputlangs->setDefaultLang($newlang);
2991 $outputlangs->load('products');
2992 }
2993 $model = $object->model_pdf;
2994 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2995 if ($result < 0) {
2996 setEventMessages($object->error, $object->errors, 'errors');
2997 }
2998 }
2999
3000 unset($_POST['prod_entry_mode']);
3001 unset($_POST['qty']);
3002 unset($_POST['type']);
3003 unset($_POST['remise_percent']);
3004 unset($_POST['price_ht']);
3005 unset($_POST['multicurrency_price_ht']);
3006 unset($_POST['price_ttc']);
3007 unset($_POST['tva_tx']);
3008 unset($_POST['product_ref']);
3009 unset($_POST['product_label']);
3010 unset($_POST['product_desc']);
3011 unset($_POST['fournprice']);
3012 unset($_POST['buying_price']);
3013 unset($_POST['np_marginRate']);
3014 unset($_POST['np_markRate']);
3015 unset($_POST['dp_desc']);
3016 unset($_POST['idprod']);
3017 unset($_POST['units']);
3018 unset($_POST['date_starthour']);
3019 unset($_POST['date_startmin']);
3020 unset($_POST['date_startsec']);
3021 unset($_POST['date_startday']);
3022 unset($_POST['date_startmonth']);
3023 unset($_POST['date_startyear']);
3024 unset($_POST['date_endhour']);
3025 unset($_POST['date_endmin']);
3026 unset($_POST['date_endsec']);
3027 unset($_POST['date_endday']);
3028 unset($_POST['date_endmonth']);
3029 unset($_POST['date_endyear']);
3030 unset($_POST['situations']);
3031 unset($_POST['progress']);
3032
3033 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
3034 exit();
3035 } else {
3036 setEventMessages($object->error, $object->errors, 'errors');
3037 }
3038
3039 $action = '';
3040 }
3041 }
3042 } elseif ($action == 'addline' && $usercancreate && (
3043 (GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '') ||
3044 (GETPOST('submitforallmark', 'alpha') && GETPOST('markforalllines', 'alpha') !== '')
3045 )) {
3046 $outlangs = $langs;
3047 $margin_rate = GETPOSTISSET('marginforalllines') ? GETPOST('marginforalllines', 'int') : '';
3048 $mark_rate = GETPOSTISSET('markforalllines') ? GETPOST('markforalllines', 'int') : '';
3049 foreach ($object->lines as &$line) {
3050 if ($line->subprice > 0) {
3051 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
3052 continue;
3053 }
3054 $subprice_multicurrency = $line->subprice;
3055 if (is_numeric($margin_rate) && $margin_rate > 0) {
3056 $line->subprice = (float) price2num((float) $line->pa_ht * (1 + (float) $margin_rate / 100), 'MU');
3057 } elseif (is_numeric($mark_rate) && $mark_rate > 0) {
3058 $line->subprice = (float) ($line->pa_ht / (1 - ((float) $mark_rate / 100)));
3059 } else {
3060 $line->subprice = (float) $line->pa_ht;
3061 }
3062
3063 if ($line->fk_product > 0) {
3064 $prod = new Product($db);
3065 $res = $prod->fetch($line->fk_product);
3066 if ($res > 0) {
3067 if ($prod->price_min > $line->subprice) {
3068 $price_subprice = price($line->subprice, 0, $outlangs, 1, -1, -1, 'auto');
3069 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
3070 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
3071 } else {
3072 setEventMessages($prod->error, $prod->errors, 'errors');
3073 }
3074 } else {
3075 setEventMessages($prod->error, $prod->errors, 'errors');
3076 }
3077 }
3078 // Manage $line->subprice and $line->multicurrency_subprice
3079 $multicurrency_subprice = (float) $line->subprice * $line->multicurrency_subprice / $subprice_multicurrency;
3080 // Update DB
3081 $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->product_ref, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $multicurrency_subprice);
3082 // Update $object with new margin info
3083 if ($result > 0) {
3084 if (is_numeric($margin_rate) && empty($mark_rate)) {
3085 $line->marge_tx = $margin_rate;
3086 } elseif (is_numeric($mark_rate) && empty($margin_rate)) {
3087 $line->marque_tx = $mark_rate;
3088 }
3089 $line->total_ht = $line->qty * (float) $line->subprice;
3090 $line->total_tva = $line->tva_tx * $line->qty * (float) $line->subprice;
3091 $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $line->subprice;
3092 // Manage $line->subprice and $line->multicurrency_subprice
3093 $line->multicurrency_total_ht = $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3094 $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3095 $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3096 // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
3097 $line->multicurrency_subprice = $multicurrency_subprice;
3098 } else {
3099 setEventMessages($object->error, $object->errors, 'errors');
3100 }
3101 }
3102 }
3103 } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
3104 // Handling updating a title line for subtotals module
3105
3106 $langs->load('subtotals');
3107
3108 $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title");
3109 $depth = GETPOSTINT('line_depth') ?? 1;
3110
3111 $subtotal_options = array();
3112
3113 foreach (Facture::$TITLE_OPTIONS as $option) {
3114 $value = GETPOST($option, 'alphanohtml');
3115 if ($value) {
3116 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
3117 }
3118 }
3119
3120 // Update line
3121 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
3122
3123 if ($result >= 0) {
3124 if ($result == 0) {
3125 setEventMessages($object->error, $object->errors, 'warnings');
3126 }
3127 $ret = $object->fetch($object->id); // Reload to get new records
3128 $object->fetch_thirdparty();
3129
3130 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3131 // Define output language
3132 $outputlangs = $langs;
3133 $newlang = GETPOST('lang_id', 'alpha');
3134 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3135 $newlang = $object->thirdparty->default_lang;
3136 }
3137 if (!empty($newlang)) {
3138 $outputlangs = new Translate("", $conf);
3139 $outputlangs->setDefaultLang($newlang);
3140 }
3141
3142 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3143 }
3144 } else {
3145 setEventMessages($object->error, $object->errors, 'errors');
3146 }
3147 } elseif ($action == 'updatetextline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
3148 // Handling updating a text line for subtotals module
3149
3150 $langs->load('subtotals');
3151
3152 $desc = GETPOST('line_desc', 'restricthtml');
3153
3154 // Update line
3155 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, 0, array());
3156
3157 if ($result >= 0) {
3158 if ($result == 0) {
3159 setEventMessages($object->error, $object->errors, 'warnings');
3160 }
3161 $ret = $object->fetch($object->id); // Reload to get new records
3162 $object->fetch_thirdparty();
3163
3164 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3165 // Define output language
3166 $outputlangs = $langs;
3167 $newlang = GETPOST('lang_id', 'alpha');
3168 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3169 $newlang = $object->thirdparty->default_lang;
3170 }
3171 if (!empty($newlang)) {
3172 $outputlangs = new Translate("", $conf);
3173 $outputlangs->setDefaultLang($newlang);
3174 }
3175
3176 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3177 }
3178 } else {
3179 setEventMessages($object->error, $object->errors, 'errors');
3180 }
3181 } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
3182 // Handling updating a subtotal line for subtotals module
3183
3184 $langs->load('subtotals');
3185
3186 $desc = GETPOST('line_desc', 'alphanohtml');
3187 $depth = GETPOSTINT('line_depth');
3188
3189 $subtotal_options = array();
3190
3191 foreach (Facture::$SUBTOTAL_OPTIONS as $option) {
3192 $value = GETPOST($option, 'alphanohtml');
3193 if ($value) {
3194 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
3195 }
3196 }
3197
3198 // Update line
3199 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
3200
3201 if ($result > 0) {
3202 $ret = $object->fetch($object->id); // Reload to get new records
3203 $object->fetch_thirdparty();
3204
3205 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3206 // Define output language
3207 $outputlangs = $langs;
3208 $newlang = GETPOST('lang_id', 'alpha');
3209 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3210 $newlang = $object->thirdparty->default_lang;
3211 }
3212 if (!empty($newlang)) {
3213 $outputlangs = new Translate("", $conf);
3214 $outputlangs->setDefaultLang($newlang);
3215 }
3216
3217 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3218 }
3219 } else {
3220 setEventMessages($object->error, $object->errors, 'errors');
3221 }
3222 } elseif ($action == 'updateline' && $usercancreate && !GETPOST('cancel', 'alpha')) {
3223 if (!$object->fetch($id) > 0) {
3225 }
3226 $object->fetch_thirdparty();
3227
3228 // Clean parameters
3229 $date_start = '';
3230 $date_end = '';
3231 $date_start = dol_mktime(GETPOSTINT('date_starthour'), GETPOSTINT('date_startmin'), GETPOSTINT('date_startsec'), GETPOSTINT('date_startmonth'), GETPOSTINT('date_startday'), GETPOSTINT('date_startyear'));
3232 $date_end = dol_mktime(GETPOSTINT('date_endhour'), GETPOSTINT('date_endmin'), GETPOSTINT('date_endsec'), GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'));
3233 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml') ? GETPOST('product_desc', 'restricthtml') : GETPOST('desc', 'restricthtml'));
3234 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
3235
3236 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
3237 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
3238
3239 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
3240 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
3241
3242 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
3243
3244 // Define info_bits
3245 $info_bits = 0;
3246 if (preg_match('/\*/', $vat_rate)) {
3247 $info_bits |= 0x01;
3248 }
3249
3250 // Define vat_rate
3251 $vat_rate = str_replace('*', '', $vat_rate);
3252 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty);
3253 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty);
3254
3255 // Add buying price
3256 $fournprice = (int) (GETPOST('fournprice') ? GETPOST('fournprice') : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
3257 $buyingprice = price2num(GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''); // If buying_price is '0', we must keep this value
3258
3259 // Prepare a price equivalent for minimum price check
3260 $pu_equivalent = $pu_ht;
3261 $pu_equivalent_ttc = $pu_ttc;
3262
3263 $currency_tx = $object->multicurrency_tx;
3264
3265 // Check if we have a foreign currency
3266 // If so, we update the pu_equiv as the equivalent price in base currency
3267 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
3268 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
3269 }
3270 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
3271 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
3272 }
3273
3274 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
3275 /*
3276 if ($pu_equivalent) {
3277 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
3278 $pu_equivalent_ttc = ...
3279 } else {
3280 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
3281 $pu_equivalent_ht = ...
3282 }
3283 */
3284
3285 // Extrafields
3286 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
3287 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
3288 // Unset extrafield
3289 if (is_array($extralabelsline)) {
3290 // Get extra fields
3291 foreach ($extralabelsline as $key => $value) {
3292 unset($_POST["options_".$key]);
3293 }
3294 }
3295
3296 // Define special_code for special lines
3297 $special_code = GETPOSTINT('special_code');
3298 if ($special_code == 3) {
3299 $special_code = 0; // Options should not exists on invoices
3300 }
3301
3302 $line = new FactureLigne($db);
3303 $line->fetch(GETPOSTINT('lineid'));
3304 $percent = $line->get_prev_progress($object->id);
3305 $progress = price2num(GETPOST('progress', 'alpha'));
3306
3307 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->situation_cycle_ref > 0) {
3308 // in case of situation credit note
3309 if ($progress >= 0) {
3310 $mesg = $langs->trans("CantBeNullOrPositive");
3311 setEventMessages($mesg, null, 'warnings');
3312 $error++;
3313 $result = -1;
3314 } elseif ($progress < $line->situation_percent) { // TODO : use a modified $line->get_prev_progress($object->id) result
3315 $mesg = $langs->trans("CantBeLessThanMinPercent");
3316 setEventMessages($mesg, null, 'warnings');
3317 $error++;
3318 $result = -1;
3319 } elseif ($progress < $percent) {
3320 $mesg = '<div class="warning">'.$langs->trans("CantBeLessThanMinPercent").'</div>';
3321 setEventMessages($mesg, null, 'warnings');
3322 $error++;
3323 $result = -1;
3324 }
3325 }
3326
3327 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
3328 if (empty($remise_percent)) {
3329 $remise_percent = 0;
3330 }
3331
3332 // The form JS clears the other field when the user edits one of them: only the modified field is filled.
3333 // When both fields are submitted, the user did not change the price - we must preserve the original
3334 // storage mode of the line, otherwise a no-op save would shift the total by rounding.
3335 $pu = $pu_ht;
3336 $price_base_type = 'HT';
3337 if (empty($pu_ht) && !empty($pu_ttc)) {
3338 $pu = $pu_ttc;
3339 $price_base_type = 'TTC';
3340 } elseif (!empty($pu_ht) && !empty($pu_ttc)) {
3341 foreach ($object->lines as $line_obj) {
3342 if ($line_obj->id == GETPOSTINT('lineid')) {
3343 // Line was originally entered in TTC mode (subprice_ttc filled by addline)
3344 if ($line_obj->wasEnteredIncludingTax()) {
3345 $pu = $pu_ttc;
3346 $price_base_type = 'TTC';
3347 }
3348 break;
3349 }
3350 }
3351 }
3352
3353 // Check minimum price
3354 $productid = GETPOSTINT('productid');
3355 if (!empty($productid)) {
3356 $product = new Product($db);
3357 $product->fetch($productid);
3358
3359 $type = $product->type;
3360
3361 $price_min = $product->price_min;
3362 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
3363 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
3364 }
3365 $price_min_ttc = $product->price_min_ttc;
3366 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
3367 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
3368 }
3369
3370 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
3371
3372 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
3373 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
3374 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
3375 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
3376 setEventMessages($mesg, null, 'errors');
3377 $error++;
3378 $action = 'editline';
3379 } elseif ($pu_equivalent_ttc && $price_min_ttc && (((float) price2num($pu_equivalent_ttc) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min_ttc)) && $price_base_type == 'TTC') {
3380 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
3381 setEventMessages($mesg, null, 'errors');
3382 $error++;
3383 $action = 'editline';
3384 }
3385 }
3386 } else {
3387 $type = GETPOST('type');
3388 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
3389
3390 // Check parameters
3391 if (GETPOST('type') < 0) {
3392 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
3393 $error++;
3394 }
3395 }
3396 if ($qty < 0 && !getDolGlobalString('INVOICE_ENABLE_NEGATIVE_QTY')) {
3397 $langs->load("errors");
3398 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
3399 $error++;
3400 }
3401 if (empty($productid) && (($pu_ht < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $pu_ht == '') && (($pu_ht_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $pu_ht_devise == '') && $pu_ttc === '' && $pu_ttc_devise === '' && $object->type != Facture::TYPE_CREDIT_NOTE) { // Unit price can be 0 but not ''
3402 if (($pu_ht < 0 || $pu_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
3403 $langs->load("errors");
3404 if ($object->type == $object::TYPE_DEPOSIT) {
3405 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
3406 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
3407 } else {
3408 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
3409 }
3410 $error++;
3411 } else {
3412 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
3413 $error++;
3414 }
3415 }
3416
3417 // Invoice situation
3418 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
3419 $previousprogress = $line->getAllPrevProgress($line->fk_facture);
3420 $fullprogress = (float) price2num(GETPOST('progress', 'alpha'), 2);
3421
3422 if ($fullprogress < $previousprogress) {
3423 $error++;
3424 setEventMessages($langs->trans('CantBeLessThanMinPercent'), null, 'errors');
3425 }
3426
3427 // Max 100%
3428 if ($fullprogress > 100) {
3429 $fullprogress = 100;
3430 }
3431 $addprogress = $fullprogress - $previousprogress;
3432 } else {
3433 $addprogress = price2num(GETPOST('progress', 'alpha'));
3434 }
3435
3436 // Update line
3437 if (!$error) {
3438 if (empty($usercancreatemargin)) {
3439 foreach ($object->lines as &$line) {
3440 if ($line->id == GETPOSTINT('lineid')) {
3441 $fournprice = $line->fk_fournprice;
3442 $buyingprice = $line->pa_ht;
3443 break;
3444 }
3445 }
3446 }
3447
3448 $result = $object->updateline(
3449 GETPOSTINT('lineid'),
3450 $description,
3451 (float) $pu,
3452 (float) $qty,
3453 (float) $remise_percent,
3455 $date_end,
3456 $vat_rate,
3457 $localtax1_rate,
3458 $localtax2_rate,
3459 $price_base_type,
3460 $info_bits,
3461 $type,
3462 GETPOSTINT('fk_parent_line'),
3463 0,
3464 (int) $fournprice,
3465 $buyingprice,
3466 $label,
3467 $special_code,
3468 $array_options,
3469 $addprogress,
3470 GETPOSTINT('units'),
3471 (float) $pu_ht_devise
3472 );
3473
3474 if ($result >= 0) {
3475 $ret = $object->fetch($id); // Reload to get new records
3476 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3477 // Define output language
3478 $outputlangs = $langs;
3479 $newlang = '';
3480 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
3481 $newlang = GETPOST('lang_id', 'aZ09');
3482 }
3483 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3484 $newlang = $object->thirdparty->default_lang;
3485 }
3486 if (!empty($newlang)) {
3487 $outputlangs = new Translate("", $conf);
3488 $outputlangs->setDefaultLang($newlang);
3489 $outputlangs->load('products');
3490 }
3491 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3492 }
3493
3494 unset($_POST['qty']);
3495 unset($_POST['type']);
3496 unset($_POST['productid']);
3497 unset($_POST['remise_percent']);
3498 unset($_POST['price_ht']);
3499 unset($_POST['multicurrency_price_ht']);
3500 unset($_POST['price_ttc']);
3501 unset($_POST['tva_tx']);
3502 unset($_POST['product_ref']);
3503 unset($_POST['product_label']);
3504 unset($_POST['product_desc']);
3505 unset($_POST['fournprice']);
3506 unset($_POST['buying_price']);
3507 unset($_POST['np_marginRate']);
3508 unset($_POST['np_markRate']);
3509 unset($_POST['dp_desc']);
3510 unset($_POST['idprod']);
3511 unset($_POST['units']);
3512 unset($_POST['date_starthour']);
3513 unset($_POST['date_startmin']);
3514 unset($_POST['date_startsec']);
3515 unset($_POST['date_startday']);
3516 unset($_POST['date_startmonth']);
3517 unset($_POST['date_startyear']);
3518 unset($_POST['date_endhour']);
3519 unset($_POST['date_endmin']);
3520 unset($_POST['date_endsec']);
3521 unset($_POST['date_endday']);
3522 unset($_POST['date_endmonth']);
3523 unset($_POST['date_endyear']);
3524 unset($_POST['situations']);
3525 unset($_POST['progress']);
3526 } else {
3527 setEventMessages($object->error, $object->errors, 'errors');
3528 }
3529 }
3530 } elseif ($action == 'updatealllines' && $usercancreate && GETPOSTISSET('all_percent')) { // Update all lines of situation invoice
3531 if (!$object->fetch($id) > 0) {
3533 }
3534 if (GETPOST('all_progress') != "") {
3535 $all_progress = GETPOSTFLOAT('all_progress');
3536 if ($all_progress > 100) {
3537 $all_progress = 100;
3538 }
3539
3540 foreach ($object->lines as $line) {
3541 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
3542 $percent = $line->getAllPrevProgress($object->id);
3543 } else {
3544 $percent = $line->get_prev_progress($object->id);
3545 }
3546 if ($object->type != $object::TYPE_CREDIT_NOTE && (float) $all_progress < (float) $percent) {
3547 $mesg = $langs->trans("Line").' '.$line->rang.' : '.$langs->trans("CantBeLessThanMinPercent");
3548 setEventMessages($mesg, null, 'warnings');
3549 $result = -1;
3550 } elseif ($object->type == $object::TYPE_CREDIT_NOTE && (float) $all_progress > (float) $percent) {
3551 $mesg = $langs->trans("Line").' '.$line->rang.' : '.$langs->trans("CantBeMoreThanMinPercent");
3552 setEventMessages($mesg, null, 'warnings');
3553 $result = -1;
3554 } else {
3555 $object->update_percent($line, $all_progress, false);
3556 }
3557 }
3558 $object->update_price(1);
3559 }
3560 } elseif ($action == 'updateline' && $usercancreate && !$cancel) {
3561 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id); // To show again edited page
3562 exit();
3563 } elseif ($action == 'confirm_situationout' && $confirm == 'yes' && $usercancreate) {
3564 // Outing situation invoice from cycle
3565 $object->fetch($id, '', '', 0, true);
3566
3567 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
3568 && $object->isSituationInvoice()
3569 && $usercancreate
3570 && !$objectidnext
3571 && $object->is_last_in_cycle()
3572 && $usercanunvalidate
3573 ) {
3574 $outingError = 0;
3575 $newCycle = $object->newCycle(); // we need to keep the "situation behavior" so we place it on a new situation cycle
3576 if ($newCycle > 1) {
3577 // Search credit notes
3578 $lastCycle = $object->situation_cycle_ref;
3579 $lastSituationCounter = $object->situation_counter;
3580 $linkedCreditNotesList = array();
3581
3582 if (count($object->tab_next_situation_invoice) > 0) {
3583 foreach ($object->tab_next_situation_invoice as $next_invoice) {
3584 if ($next_invoice->type == Facture::TYPE_CREDIT_NOTE
3585 && $next_invoice->situation_counter == $object->situation_counter
3586 && $next_invoice->fk_facture_source == $object->id
3587 ) {
3588 $linkedCreditNotesList[] = $next_invoice->id;
3589 }
3590 }
3591 }
3592
3593 $object->situation_cycle_ref = $newCycle;
3594 $object->situation_counter = 1;
3595 $object->situation_final = 0;
3596 if ($object->update($user) > 0) {
3597 $errors = 0;
3598 if (count($linkedCreditNotesList) > 0) {
3599 // now, credit note must follow
3600 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3601 $sql .= ' SET situation_cycle_ref = '.((int) $newCycle);
3602 $sql .= ' , situation_final=0';
3603 $sql .= ' , situation_counter='.((int) $object->situation_counter);
3604 $sql .= ' WHERE rowid IN ('.$db->sanitize(implode(',', $linkedCreditNotesList)).')';
3605
3606 $resql = $db->query($sql);
3607 if (!$resql) {
3608 $errors++;
3609 }
3610
3611 // Change each progression percent on each lines
3612 foreach ($object->lines as $line) {
3613 // no processing for special product
3614 if ($line->product_type == 9) {
3615 continue;
3616 }
3617
3618
3619 if (!empty($object->tab_previous_situation_invoice)) {
3620 // search the last invoice in cycle
3621 $lineIndex = count($object->tab_previous_situation_invoice) - 1;
3622 $searchPreviousInvoice = true;
3623 while ($searchPreviousInvoice) {
3624 if ($object->tab_previous_situation_invoice[$lineIndex]->isSituationInvoice() || $lineIndex < 1) {
3625 $searchPreviousInvoice = false; // find, exit;
3626 break;
3627 } else {
3628 $lineIndex--; // go to previous invoice in cycle
3629 }
3630 }
3631
3632
3633 $maxPrevSituationPercent = 0;
3634 foreach ($object->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
3635 if ($prevLine->id == $line->fk_prev_id) {
3636 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
3637 }
3638 }
3639
3640
3641 $line->situation_percent -= $maxPrevSituationPercent;
3642
3643 if ($line->update() < 0) {
3644 $errors++;
3645 }
3646 }
3647 }
3648 }
3649
3650 if (!$errors) {
3651 setEventMessages($langs->trans('Updated'), null, 'mesgs');
3652 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
3653 } else {
3654 setEventMessages($langs->trans('ErrorOutingSituationInvoiceCreditNote'), array(), 'errors');
3655 }
3656 } else {
3657 setEventMessages($langs->trans('ErrorOutingSituationInvoiceOnUpdate'), array(), 'errors');
3658 }
3659 } else {
3660 setEventMessages($langs->trans('ErrorFindNextSituationInvoice'), array(), 'errors');
3661 }
3662 }
3663 } elseif ($action == 'import_lines_from_object' && $usercancreate && $object->status == Facture::STATUS_DRAFT
3665 // add lines from objectlinked
3666 $fromElement = GETPOST('fromelement');
3667 $fromElementid = GETPOST('fromelementid');
3668 $importLines = GETPOST('line_checkbox');
3669
3670 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && ctype_alpha($fromElement) && !empty($fromElementid)) {
3671 $lineClassName = '';
3672 if ($fromElement == 'commande') {
3673 dol_include_once('/'.$fromElement.'/class/'.$fromElement.'.class.php');
3674 $lineClassName = 'OrderLine';
3675 } elseif ($fromElement == 'propal') {
3676 dol_include_once('/comm/'.$fromElement.'/class/'.$fromElement.'.class.php');
3677 $lineClassName = 'PropaleLigne';
3678 }
3679 $nextRang = count($object->lines) + 1;
3680 $importCount = 0;
3681 $error = 0;
3682 foreach ($importLines as $lineId) {
3683 if ($lineClassName === '') {
3684 // No class
3685 if ($error === 0) {
3686 // Log only once
3687 dol_syslog('compta/facture/card - No lineClassName - skip import', LOG_ERR);
3688 }
3689 // Ensure we report that all line failed (see error message below)
3690 $error++;
3691 break;
3692 }
3693
3694 $lineId = intval($lineId);
3695 $originLine = new $lineClassName($db);
3696 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
3697 $originLine->fetch_optionals();
3698 $desc = $originLine->desc;
3699 $pu_ht = $originLine->subprice;
3700 $qty = $originLine->qty;
3701 $txtva = $originLine->tva_tx;
3702 $txlocaltax1 = $originLine->localtax1_tx;
3703 $txlocaltax2 = $originLine->localtax2_tx;
3704 $fk_product = $originLine->fk_product;
3705 $remise_percent = $originLine->remise_percent;
3706 $date_start = $originLine->date_start;
3707 $date_end = $originLine->date_end;
3708 $fk_code_ventilation = 0;
3709 $info_bits = $originLine->info_bits;
3710 $fk_remise_except = $originLine->fk_remise_except;
3711 $price_base_type = 'HT';
3712 $pu_ttc = 0;
3713 $type = $originLine->product_type;
3714 $rang = $nextRang++;
3715 $special_code = $originLine->special_code;
3716 $origin = $originLine->element;
3717 $origin_id = $originLine->id;
3718 $fk_parent_line = 0;
3719 $fk_fournprice = $originLine->fk_fournprice;
3720 $pa_ht = $originLine->pa_ht;
3721 $label = $originLine->label;
3722 $array_options = $originLine->array_options;
3723 if ($object->isSituationInvoice()) {
3724 $situation_percent = 0;
3725 } else {
3726 $situation_percent = 100;
3727 }
3728 $fk_prev_id = 0;
3729 $fk_unit = $originLine->fk_unit;
3730 $pu_ht_devise = $originLine->multicurrency_subprice;
3731
3732 $res = $object->addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $fk_code_ventilation, $info_bits, $fk_remise_except, $price_base_type, $pu_ttc, $type, $rang, $special_code, $origin, $origin_id, $fk_parent_line, $fk_fournprice, $pa_ht, $label, $array_options, $situation_percent, $fk_prev_id, $fk_unit, $pu_ht_devise);
3733
3734 if ($res > 0) {
3735 $importCount++;
3736 } else {
3737 $error++;
3738 }
3739 } else {
3740 $error++;
3741 }
3742 }
3743
3744 if ($error) {
3745 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
3746 }
3747 }
3748 }
3749
3750
3751 // Actions when printing a doc from card
3752 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
3753
3754 // Actions to send emails
3755 if (empty($id)) {
3756 $id = $facid;
3757 }
3758 if (!empty($object->id) && $action == 'send') { // Test on permission not required
3759 // load totalpaid, totaldeposits, totalcreditnotes that can be used in email templates
3760 $object->getSommePaiement(-1);
3761 $object->getSumCreditNotesUsed(-1);
3762 $object->getSumDepositsUsed(-1);
3763 }
3764 $triggersendname = 'BILL_SENTBYMAIL';
3765 $paramname = 'id';
3766 $autocopy = 'MAIN_MAIL_AUTOCOPY_INVOICE_TO';
3767 $trackid = 'inv'.$object->id;
3768 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
3769
3770 // Actions to build doc
3771 $upload_dir = $conf->invoice->multidir_output[!empty($object->entity) ? $object->entity : $conf->entity];
3772 $permissiontoadd = $usercancreate;
3773 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
3774
3775
3776 if ($action == 'update_extras' && $permissiontoeditextra) {
3777 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
3778
3779 $attribute_name = GETPOST('attribute', 'aZ09');
3780
3781 // Fill array 'array_options' with data from add form
3782 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
3783 if ($ret < 0) {
3784 $error++;
3785 }
3786
3787 if (!$error) {
3788 // Actions on extra fields
3789 $result = $object->updateExtraField($attribute_name, 'BILL_MODIFY');
3790 if ($result < 0) {
3791 setEventMessages($object->error, $object->errors, 'errors');
3792 $error++;
3793 }
3794 }
3795
3796 if ($error) {
3797 $action = 'edit_extras';
3798 }
3799 }
3800
3801 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3802 if ($action == 'addcontact' && $usercancreate) {
3803 $result = $object->fetch($id);
3804
3805 if ($result > 0 && $id > 0) {
3806 $contactid = (GETPOST('userid') ? GETPOSTINT('userid') : GETPOSTINT('contactid'));
3807 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
3808 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
3809 }
3810
3811 if ($result >= 0) {
3812 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3813 exit();
3814 } else {
3815 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
3816 $langs->load("errors");
3817 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
3818 } else {
3819 setEventMessages($object->error, $object->errors, 'errors');
3820 }
3821 }
3822 } elseif ($action == 'swapstatut' && $usercancreate) {
3823 // toggle the status of a contact
3824 if ($object->fetch($id)) {
3825 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
3826 } else {
3828 }
3829 } elseif ($action == 'deletecontact' && $usercancreate) {
3830 // Delete a contact
3831 $object->fetch($id);
3832 $result = $object->delete_contact($lineid);
3833
3834 if ($result >= 0) {
3835 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3836 exit();
3837 } else {
3839 }
3840 }
3841
3842 if ($error) {
3843 $action = 'edit_extras';
3844 }
3845 }
3846}
3847
3848
3849/*
3850 * View
3851 */
3852
3853$form = new Form($db);
3854$formother = new FormOther($db);
3855$formfile = new FormFile($db);
3856$formmargin = new FormMargin($db);
3857$soc = new Societe($db);
3858$paymentstatic = new Paiement($db);
3859$bankaccountstatic = new Account($db);
3860$formproject = null;
3861if (isModEnabled('project')) {
3862 $formproject = new FormProjets($db);
3863}
3864
3865$now = dol_now();
3866
3867$title = $object->ref." - ".$langs->trans('Card');
3868if ($action == 'create') {
3869 $title = $langs->trans("NewBill");
3870}
3871$help_url = "EN:Customers_Invoices|FR:Factures_Clients|ES:Facturas_a_clientes";
3872
3873llxHeader('', $title, $help_url);
3874
3875// Mode creation
3876
3877if ($action == 'create') {
3878 $facturestatic = new Facture($db);
3879 $extrafields->fetch_name_optionals_label($facturestatic->table_element);
3880
3881 print load_fiche_titre($langs->trans('NewBill'), '', 'bill');
3882
3883 if ($socid > 0) {
3884 $res = $soc->fetch($socid);
3885 }
3886
3887 $currency_code = $conf->currency;
3888
3889 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
3890 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
3891 $fk_account = GETPOSTINT('fk_account');
3892
3893 $dateinvoice = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
3894 if (empty($dateinvoice)) {
3895 $dateinvoice = (getDolGlobalString('MAIN_DO_NOT_AUTOFILL_DATE_INVOICE') ? -1 : ''); // By default '' so we will autofill date. -1 means keep empty.
3896 }
3897
3898 // Load objectsrc
3899 $objectsrc = null;
3900 if (!empty($origin) && !empty($originid)) {
3901 // Parse element/subelement (ex: project_task)
3902 $element = $subelement = $origin;
3903 $regs = array();
3904 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
3905 $element = $regs[1];
3906 $subelement = $regs[2];
3907 }
3908
3909 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
3910
3911 if ($element == 'project') {
3912 $projectid = $originid;
3913
3914 if (empty($cond_reglement_id)) {
3915 $cond_reglement_id = $soc->cond_reglement_id;
3916 }
3917 if (empty($mode_reglement_id)) {
3918 $mode_reglement_id = $soc->mode_reglement_id;
3919 }
3920 if (empty($fk_account)) {
3921 $fk_account = $soc->fk_account;
3922 }
3923 } else {
3924 // For compatibility
3925 if ($element == 'order' || $element == 'commande') {
3926 $element = $subelement = 'commande';
3927 }
3928 if ($element == 'propal') {
3929 $element = 'comm/propal';
3930 $subelement = 'propal';
3931 }
3932 if ($element == 'contract') {
3933 $element = $subelement = 'contrat';
3934 }
3935 if ($element == 'shipping') {
3936 $element = $subelement = 'expedition';
3937 }
3938
3939 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
3940
3941 $classname = ucfirst($subelement);
3942 $objectsrc = new $classname($db);
3943 '@phan-var-force Commande|Propal|Contrat|Expedition|Facture $objectsrc';
3945 $objectsrc->fetch($originid);
3946 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
3947 $objectsrc->fetch_lines();
3948 }
3949 $objectsrc->fetch_thirdparty();
3950
3951 $projectid = (!empty($projectid) ? $projectid : $objectsrc->fk_project);
3952
3953 // Propagate ref customer of src object to the invoice ?
3954 if (getDolGlobalString("INVOICE_DO_NOT_PROPAGATE_REF_CUSTOMER_OF_SRC_TO_INVOICE")) {
3955 $ref_client = "";
3956 } else {
3957 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : ''));
3958 }
3959
3960 // Only if socid not filled else it's already done above
3961 if (empty($socid)) {
3962 $soc = $objectsrc->thirdparty;
3963 }
3964
3965 if ($element == 'expedition') {
3966 $elem = $subelem = $objectsrc->origin_type;
3967 $expeoriginid = $objectsrc->origin_id;
3968 dol_include_once('/'.$elem.'/class/'.$subelem.'.class.php');
3969 $classname = ucfirst($subelem);
3970
3971 $expesrc = new $classname($db);
3972 '@phan-var-force Expedition $expesrc';
3974 dol_syslog("Is type Facture|Commande or Expedition: $element...expesrc($classname)=".get_class($expesrc));
3975 $expesrc->fetch($expeoriginid);
3976
3977 $cond_reglement_id = (!empty($expesrc->cond_reglement_id) ? $expesrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 1));
3978 $mode_reglement_id = (!empty($expesrc->mode_reglement_id) ? $expesrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
3979 $fk_account = (!empty($expesrc->fk_account) ? $expesrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : 0));
3980
3981 if (isModEnabled('multicurrency')) {
3982 $currency_code = (!empty($expesrc->multicurrency_code) ? $expesrc->multicurrency_code : (!empty($soc->multicurrency_code) ? $soc->multicurrency_code : $objectsrc->multicurrency_code));
3983 $currency_tx = (!empty($expesrc->multicurrency_tx) ? $expesrc->multicurrency_tx : (!empty($soc->multicurrency_tx) ? $soc->multicurrency_tx : $objectsrc->multicurrency_tx));
3984 }
3985
3986 // replicate input reason
3987 $inputReasonId = (!empty($objectsrc->demand_reason_id) ? $objectsrc->demand_reason_id : (!empty($soc->demand_reason_id) ? $soc->demand_reason_id : 0));
3988
3989 //Replicate extrafields
3990 $expesrc->fetch_optionals();
3991 $object->array_options = $expesrc->array_options;
3992 } else {
3993 $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : (!empty($cond_reglement_id) ? $cond_reglement_id : 0)));
3994 $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : (!empty($mode_reglement_id) ? $mode_reglement_id : 0)));
3995 $fk_account = (!empty($objectsrc->fk_account) ? $objectsrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : (!empty($fk_account) ? $fk_account : 0)));
3996
3997 if (isModEnabled('multicurrency')) {
3998 if (!empty($objectsrc->multicurrency_code)) {
3999 $currency_code = $objectsrc->multicurrency_code;
4000 }
4001 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
4002 $currency_tx = $objectsrc->multicurrency_tx;
4003 }
4004 }
4005
4006 // replicate input reason
4007 $inputReasonId = (!empty($objectsrc->demand_reason_id) ? $objectsrc->demand_reason_id : (!empty($soc->demand_reason_id) ? $soc->demand_reason_id : 0));
4008
4009 // Replicate extrafields
4010 $objectsrc->fetch_optionals();
4011 $object->array_options = $objectsrc->array_options;
4012 }
4013 }
4014 } else {
4015 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
4016 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
4017 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
4018 $inputReasonId = empty($soc->demand_reason_id) ? $inputReasonId : $soc->demand_reason_id;
4019
4020 if (isModEnabled('multicurrency') && !empty($soc->multicurrency_code)) {
4021 $currency_code = $soc->multicurrency_code;
4022 }
4023 }
4024
4025 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
4026 if (!GETPOST('changecompany')) {
4027 if (GETPOSTISSET('cond_reglement_id')) {
4028 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
4029 }
4030 if (GETPOSTISSET('mode_reglement_id')) {
4031 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
4032 }
4033 if (GETPOSTISSET('cond_reglement_id')) {
4034 $fk_account = GETPOSTINT('fk_account');
4035 }
4036 }
4037
4038 // when payment condition is empty (means not override by payment condition form a other object, like third-party), try to use default value
4039 if (empty($cond_reglement_id)) {
4040 $cond_reglement_id = GETPOSTINT("cond_reglement_id");
4041 }
4042
4043 // when payment mode is empty (means not override by payment mode form a other object, like third-party), try to use default value
4044 if (empty($mode_reglement_id)) {
4045 $mode_reglement_id = GETPOSTINT("mode_reglement_id");
4046 }
4047
4048 // when bank account is empty (means not override by payment mode form a other object, like third-party), try to use default value
4049 // if ($socid > 0 && $fk_account) { // A company has already been set and it has a default fk_account
4050 // $fk_account = GETPOSTISSET('fk_account') ? GETPOST("fk_account", 'int') : $fk_account; // The GETPOST is used only if form was posted to avoid to take default value, because in such case, the default must be the one of the company
4051 // } else { // No company forced
4052 // $fk_account = GETPOST("fk_account", 'int');
4053 // }
4054
4055 if (!empty($soc->id)) {
4056 $absolute_discount = $soc->getAvailableDiscounts();
4057 }
4058 $note_public = $object->getDefaultCreateValueFor('note_public', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_public : null));
4059 $note_private = $object->getDefaultCreateValueFor('note_private', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_private : null));
4060
4061 if (!empty($conf->use_javascript_ajax)) {
4062 require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
4063 print ajax_combobox('fac_replacement');
4064 print ajax_combobox('fac_avoir');
4065 print ajax_combobox('situations');
4066 }
4067
4068 if ($origin == 'contrat') {
4069 $langs->load("admin");
4070 $text = $langs->trans("ToCreateARecurringInvoice");
4071 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGene", $langs->transnoentitiesnoconv("MenuFinancial"), $langs->transnoentitiesnoconv("BillsCustomers"), $langs->transnoentitiesnoconv("ListOfTemplates"));
4072 if (!getDolGlobalString('INVOICE_DISABLE_AUTOMATIC_RECURRING_INVOICE')) {
4073 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGeneAuto", $langs->transnoentitiesnoconv('Module2300Name'));
4074 }
4075 print info_admin($text, 0, 0, 'info', '').'<br>';
4076 }
4077
4078 print '<form name="add" action="'.$_SERVER["PHP_SELF"].'" method="POST" id="formtocreate" name="formtocreate">';
4079 print '<input type="hidden" name="token" value="'.newToken().'">';
4080 print '<input type="hidden" name="action" id="formtocreateaction" value="add">';
4081 print '<input type="hidden" name="changecompany" value="0">'; // will be set to 1 by javascript so we know post is done after a company change
4082 if ($soc->id > 0) {
4083 print '<input type="hidden" name="socid" value="'.$soc->id.'">'."\n";
4084 }
4085 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
4086 print '<input name="ref" type="hidden" value="provisoire">';
4087 print '<input name="ref_client" type="hidden" value="'.$ref_client.'">';
4088 print '<input name="force_cond_reglement_id" type="hidden" value="0">';
4089 print '<input name="force_mode_reglement_id" type="hidden" value="0">';
4090 print '<input name="force_fk_account" type="hidden" value="0">';
4091 print '<input type="hidden" name="origin" value="'.$origin.'">';
4092 print '<input type="hidden" name="originid" value="'.$originid.'">';
4093 print '<input type="hidden" name="originentity" value="'.GETPOSTINT('originentity').'">';
4094 if (!empty($currency_tx)) {
4095 print '<input type="hidden" name="originmulticurrency_tx" value="'.$currency_tx.'">';
4096 }
4097
4098 print dol_get_fiche_head();
4099
4100 // Call Hook tabContentCreateInvoice
4101 $parameters = array();
4102 // Note that $action and $object may be modified by hook
4103 $reshook = $hookmanager->executeHooks('tabContentCreateInvoice', $parameters, $object, $action);
4104 if (empty($reshook)) {
4105 print '<table class="border centpercent">';
4106
4107 $exampletemplateinvoice = new FactureRec($db);
4108 $invoice_predefined = new FactureRec($db);
4109 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
4110 $invoice_predefined->fetch(GETPOSTINT('fac_rec'));
4111 }
4112
4113 // Ref
4114 /*
4115 print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans('Ref').'</td>';
4116 print '<td colspan="2">';
4117 print $langs->trans("Draft");
4118 print '</td>';
4119 print '</tr>'."\n";
4120 */
4121
4122 // Thirdparty
4123 if ($soc->id > 0 && (!GETPOSTINT('fac_rec') || !empty($invoice_predefined->frequency))) {
4124 // If thirdparty known and not a predefined invoiced without a recurring rule
4125 print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans('Customer').'</td>';
4126 print '<td colspan="2">';
4127 print $soc->getNomUrl(1, 'customer');
4128 print '<input type="hidden" name="socid" value="'.$soc->id.'">';
4129 // Outstanding Bill
4130 $arrayoutstandingbills = $soc->getOutstandingBills();
4131 $outstandingBills = $arrayoutstandingbills['opened'];
4132 print ' - <span class="opacitymedium valignmiddle">'.$langs->trans('CurrentOutstandingBill').':</span> ';
4133 print '<span class="amount valignmiddle">'.price($outstandingBills, 0, $langs, 0, 0, -1, $conf->currency).'</span>';
4134 if ($soc->outstanding_limit != '') {
4135 if ($outstandingBills > $soc->outstanding_limit) {
4136 print img_warning($langs->trans("OutstandingBillReached"));
4137 }
4138 print ' / '.price($soc->outstanding_limit, 0, $langs, 0, 0, -1, $conf->currency);
4139 }
4140 print '</td>';
4141 print '</tr>'."\n";
4142 } else {
4143 print '<tr><td class="fieldrequired">'.$langs->trans('Customer').'</td>';
4144 print '<td colspan="2">';
4145 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
4146 print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company($soc->id, 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 widthcentpercentminusxx maxwidth500');
4147 // Option to reload page to retrieve customer information.
4148 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
4149 print '<script>
4150 $(document).ready(function() {
4151 $("#socid").change(function() {
4152 /*
4153 console.log("Submit page");
4154 $(\'input[name="action"]\').val(\'create\');
4155 $(\'input[name="force_cond_reglement_id"]\').val(\'1\');
4156 $(\'input[name="force_mode_reglement_id"]\').val(\'1\');
4157 $(\'input[name="force_fk_account"]\').val(\'1\');
4158 $("#formtocreate").submit(); */
4159
4160 // For company change, we must submit page with action=create instead of action=add
4161 console.log("We have changed the company - Resubmit page");
4162 jQuery("input[name=changecompany]").val("1");
4163 jQuery("#formtocreateaction").val("create");
4164 jQuery("#formtocreate").submit();
4165 });
4166 });
4167 </script>';
4168 }
4169 if (!GETPOSTINT('fac_rec')) {
4170 print ' <a class="valignmiddle" href="'.DOL_URL_ROOT.'/societe/card.php?action=create&customer=3&fournisseur=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create').'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
4171 }
4172 print '</td>';
4173 print '</tr>'."\n";
4174 }
4175
4176 // Overwrite some values if creation of invoice is from a predefined invoice
4177 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
4178 //$invoice_predefined->fetch(GETPOSTINT('fac_rec'));
4179 foreach ($invoice_predefined->array_options as $key => $option) {
4180 if (!isset($object->array_options[$key])) {
4181 $object->array_options[$key] = $invoice_predefined->array_options[$key];
4182 }
4183 }
4184
4185 $dateinvoice = $invoice_predefined->date_when; // To use next gen date by default later
4186 if (empty($projectid)) {
4187 $projectid = $invoice_predefined->fk_project;
4188 }
4189 $cond_reglement_id = $invoice_predefined->cond_reglement_id;
4190 $mode_reglement_id = $invoice_predefined->mode_reglement_id;
4191 $fk_account = $invoice_predefined->fk_account;
4192 $note_public = $invoice_predefined->note_public;
4193 $note_private = $invoice_predefined->note_private;
4194
4195 if (!empty($invoice_predefined->multicurrency_code)) {
4196 $currency_code = $invoice_predefined->multicurrency_code;
4197 }
4198 if (!empty($invoice_predefined->multicurrency_tx)) {
4199 $currency_tx = $invoice_predefined->multicurrency_tx;
4200 }
4201
4202 $sql = 'SELECT r.rowid, r.titre as title, r.total_ttc';
4203 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_rec as r';
4204 $sql .= ' WHERE r.fk_soc = '.((int) $invoice_predefined->socid);
4205
4206 $resql = $db->query($sql);
4207 if ($resql) {
4208 $num = $db->num_rows($resql);
4209 $i = 0;
4210
4211 if ($num > 0) {
4212 print '<tr><td>'.$langs->trans('CreateFromRepeatableInvoice').'</td><td>';
4213 //print '<input type="hidden" name="fac_rec" id="fac_rec" value="'.GETPOST('fac_rec', 'int').'">';
4214 print '<select class="flat" id="fac_rec" name="fac_rec">'; // We may want to change the template to use
4215 print '<option value="0" selected></option>';
4216 while ($i < $num) {
4217 $objp = $db->fetch_object($resql);
4218 print '<option value="'.$objp->rowid.'"';
4219 if (GETPOSTINT('fac_rec') == $objp->rowid) {
4220 print ' selected';
4221 $exampletemplateinvoice->fetch(GETPOSTINT('fac_rec'));
4222 }
4223 print '>'.$objp->title.' ('.price($objp->total_ttc).' '.$langs->trans("TTC").')</option>';
4224 $i++;
4225 }
4226 print '</select>';
4227
4228 print ajax_combobox("fac_rec");
4229
4230 // Option to reload page to retrieve customer information. Note, this clear other input
4231 if (!getDolGlobalString('RELOAD_PAGE_ON_TEMPLATE_CHANGE_DISABLED')) {
4232 print '<script type="text/javascript">
4233 $(document).ready(function() {
4234 $("#fac_rec").change(function() {
4235 console.log("We have changed the template invoice - Reload page");
4236 var fac_rec = $(this).val();
4237 var socid = $(\'#socid\').val();
4238 // For template invoice change, we must reuse data of template, not input already done, so we call a GET with action=create, not a POST submit.
4239 window.location.href = "'.$_SERVER["PHP_SELF"].'?action=create&socid="+socid+"&fac_rec="+fac_rec;
4240 });
4241 });
4242 </script>';
4243 }
4244 print '</td></tr>';
4245 }
4246 $db->free($resql);
4247 } else {
4249 }
4250 }
4251
4252 print '<tr><td class="tdtop fieldrequired titlefieldcreate">'.$langs->trans('Type').'</td><td colspan="2">';
4253 print '<div class="listofinvoicetypetable">'."\n";
4254
4255 // Standard invoice
4256 print '<div class="listofinvoicetype"><div class="">';
4257 $tmp = '<input type="radio" id="radio_standard" name="type" value="0"'.(GETPOSTINT('type') ? '' : ' checked').'> ';
4258 $tmp .= '<label for="radio_standard" >'.$langs->trans("InvoiceStandardAsk");
4259 $tmp .= ' <span class="opacitymedium">'.$langs->trans("InvoiceStandardAsk2").'</span>';
4260 $tmp .= '</label>';
4261 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4262 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceStandardDesc").'<br><br>'.$langs->transnoentities("YouMustCreateStandardInvoiceFirstDesc"), 1, 'help', 'nowraponall', 0, 3, 'standardonsmartphone');
4263 print $desc;
4264 if ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid))) {
4265 /*print '<td class="nowrap" style="padding-left: 5px">';
4266 $arraylist = array(
4267 //'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
4268 //'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
4269 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4270 );
4271 print $form->selectarray('typestandard', $arraylist, GETPOST('typestandard', 'aZ09'), 0, 0, 0, '', 1);
4272 print '</td>';*/
4273 if (!getDolGlobalInt('INVOICE_DEPOSIT_INVOICE_ONLY_SAME_LINES')) {
4274 print '<span class="opacitymedium marginleftonly">' . $langs->trans('PercentOfOriginalObject') . '</span>:<input class="right" placeholder="100%" type="text" id="valuestandardinvoice" name="valuestandardinvoice" size="3" value="' . (GETPOSTISSET('valuestandardinvoice') ? GETPOST('valuestandardinvoice', 'alpha') : '100%') . '"/>';
4275 }
4276 }
4277 print '</div></div>'."\n";
4278
4279 if ((empty($origin)) || ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid)))) {
4280 // Deposit - Down payment
4281 if (!getDolGlobalString('INVOICE_DISABLE_DEPOSIT')) {
4282 print '<div class="listofinvoicetype"><div class="">';
4283 $tmp = '<input type="radio" id="radio_deposit" name="type" value="3"'.(GETPOSTINT('type') == 3 ? ' checked' : '').'> ';
4284 print '<script type="text/javascript">
4285 jQuery(document).ready(function() {
4286 jQuery("#typestandardinvoice, #valuestandardinvoice").click(function() {
4287 jQuery("#radio_standard").prop("checked", true);
4288 });
4289 jQuery("#typedeposit, #valuedeposit").click(function() {
4290 jQuery("#radio_deposit").prop("checked", true);
4291 });
4292 jQuery("#typedeposit").change(function() {
4293 console.log("We change type of down payment: "+jQuery("#typedeposit").val());
4294
4295 if (jQuery("#typedeposit").val() == "amount") {
4296 jQuery("#valuedeposit").attr("placeholder", "'.$langs->getCurrencySymbol($conf->currency).'");
4297 jQuery("#valuedeposit").val("");
4298 } else {
4299 jQuery("#valuedeposit").attr("placeholder", "50%");
4300 jQuery("#valuedeposit").val("");
4301 }
4302
4303 jQuery("#radio_deposit").prop("checked", true);
4304 setRadioForTypeOfInvoice();
4305 });
4306 jQuery("#radio_standard, #radio_deposit, #radio_replacement, #radio_creditnote, #radio_template").change(function() {
4307 setRadioForTypeOfInvoice();
4308 });
4309
4310 function setRadioForTypeOfInvoice() {
4311 console.log("Change radio for type of invoice");
4312 if (jQuery("#radio_deposit").prop("checked") && (jQuery("#typedeposit").val() == \'amount\' || jQuery("#typedeposit").val() == \'variable\')) {
4313 jQuery("#checkforselects").prop("disabled", true);
4314 jQuery("#checkforselects").prop("checked", false);
4315 jQuery(".checkforselect").prop("disabled", true);
4316 jQuery(".checkforselect").prop("checked", false);
4317 } else {
4318 jQuery("#checkforselects").prop("disabled", false);
4319 jQuery("#checkforselects").prop("checked", true);
4320 jQuery(".checkforselect").prop("disabled", false);
4321 jQuery(".checkforselect").prop("checked", true);
4322 }
4323 }
4324 });
4325 </script>';
4326
4327 $tmp = $tmp.'<label for="radio_deposit">'.$langs->trans("InvoiceDeposit").'</label>';
4328 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4329 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceDepositDesc"), 1, 'help', '', 0, 3, 'depositonsmartphone');
4330 print $desc;
4331 if (($origin == 'propal') || ($origin == 'commande')) {
4332 if (!getDolGlobalInt('INVOICE_DEPOSIT_INVOICE_ONLY_SAME_LINES')) {
4333 $arraylist = array(
4334 'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
4335 'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
4336 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4337 );
4338 } else {
4339 $arraylist = array(
4340 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4341 );
4342 }
4343
4344 $typedeposit = GETPOST('typedeposit', 'aZ09');
4345 $valuedeposit = GETPOSTFLOAT('valuedeposit');
4346 if (empty($typedeposit) && !empty($objectsrc->deposit_percent)) {
4347 $origin_payment_conditions_deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $objectsrc->cond_reglement_id);
4348 if (!empty($origin_payment_conditions_deposit_percent)) {
4349 $typedeposit = 'variable';
4350 }
4351 }
4352 if (empty($valuedeposit) && $typedeposit == 'variable' && !empty($objectsrc->deposit_percent)) {
4353 $valuedeposit = $objectsrc->deposit_percent;
4354 }
4355 print '<span class="marginleftonly">'.$form->selectarray('typedeposit', $arraylist, $typedeposit, 0, 0, 0, '', 1).'</span>';
4356 //print '<span class="opacitymedium paddingleft">'.$langs->trans("AmountOrPercent").'</span>';
4357 print '<input type="text" id="valuedeposit" name="valuedeposit" class="width75 right" value="'.($valuedeposit ? $valuedeposit : '').'"'.($valuedeposit ? '' : 'placeholder="'.$langs->getCurrencySymbol($conf->currency).'"').'>';
4358 }
4359
4360 print '</div></div>'."\n";
4361 }
4362 }
4363
4364 if ($socid > 0) {
4365 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
4366 // First situation invoice
4367 print '<div class="listofinvoicetype"><div class="">';
4368 $tmp = '<input id="radio_situation" type="radio" name="type" value="5"'.(GETPOST('type') == 5 ? ' checked' : '').'> ';
4369 $tmp = $tmp.'<label for="radio_situation" >'.$langs->trans("InvoiceFirstSituationAsk").'</label>';
4370 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4371 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceFirstSituationDesc"), 1, 'help', '', 0, 3, 'firstsituationonsmartphone');
4372 print $desc;
4373
4374 // Next situation invoice
4375 $opt = $form->selectSituationInvoices((string) GETPOSTINT('originid'), $socid);
4376
4377 //print ' &nbsp; ';
4378 print '</div></div><div class="listofinvoicetype"><div>';
4379
4380 $tmp = '<input id="radio_situation_bis" type="radio" name="type" value="5"'.(GETPOST('type') == 5 && GETPOSTINT('originid') ? ' checked' : '');
4381 if ($opt == ('<option value="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
4382 $tmp .= ' disabled';
4383 }
4384 $tmp .= '> ';
4385 $text = $tmp.'<label for="radio_situation_bis">'.$langs->trans("InvoiceSituationAsk").'</label> ';
4386
4387 $text .= '<select class="flat minwidth125" id="situations" name="situations"';
4388 if ($opt == ('<option value="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
4389 $text .= ' disabled';
4390 }
4391 $text .= '>';
4392 $text .= $opt;
4393 $text .= '</select>';
4394 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceSituationDesc"), 1, 'help', '', 0, 3);
4395 print $desc;
4396 print '</div></div>'."\n";
4397 }
4398
4399 // Replacement
4400 if (!getDolGlobalString('INVOICE_DISABLE_REPLACEMENT')) {
4401 // Type de facture
4402 $facids = $facturestatic->list_replacable_invoices($soc->id);
4403 if ($facids < 0) {
4404 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
4405 exit();
4406 }
4407 $options = "";
4408 if (is_array($facids)) {
4409 foreach ($facids as $facparam) {
4410 $options .= '<option value="'.$facparam ['id'].'"';
4411 if ($facparam['id'] == GETPOSTINT('fac_replacement')) {
4412 $options .= ' selected';
4413 }
4414 $options .= '>'.$facparam['ref'];
4415 $options .= ' ('.$facturestatic->LibStatut($facparam['paid'], $facparam['status'], 0, $facparam['alreadypaid']).')';
4416 $options .= '</option>';
4417 }
4418 }
4419
4420 print '<!-- replacement line -->';
4421 print '<div class="listofinvoicetype"><div class="">';
4422 $tmp = '<input type="radio" name="type" id="radio_replacement" value="1"'.(GETPOST('type') == 1 ? ' checked' : '');
4423 if (!$options || $invoice_predefined->id > 0) {
4424 $tmp .= ' disabled';
4425 }
4426 $tmp .= '> ';
4427 print '<script type="text/javascript">
4428 jQuery(document).ready(function() {
4429 jQuery("#fac_replacement").change(function() {
4430 console.log("We change fac_replacement");
4431 jQuery("#radio_replacement").prop("checked", true);
4432 });
4433 });
4434 </script>';
4435 $text = $tmp.'<label for="radio_replacement">'.$langs->trans("InvoiceReplacementAsk").'</label>';
4436 $text .= '<select class="flat" name="fac_replacement" id="fac_replacement"';
4437 if (!$options || $invoice_predefined->id > 0) {
4438 $text .= ' disabled';
4439 }
4440 $text .= '>';
4441 if ($options) {
4442 $text .= '<option value="-1">&nbsp;</option>';
4443 $text .= $options;
4444 } else {
4445 $text .= '<option value="-1">'.$langs->trans("NoReplacableInvoice").'</option>';
4446 }
4447 $text .= '</select>';
4448 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc"), 1, 'help', '', 0, 3);
4449 print $desc;
4450 print '</div></div>'."\n";
4451 }
4452 } else {
4453 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
4454 print '<div class="listofinvoicetype"><div class="">';
4455 $tmp = '<input type="radio" name="type" id="radio_situation" value="0" disabled> ';
4456 $text = $tmp.'<label class="opacitymedium">'.$langs->trans("InvoiceSituationAsk").'</label> ';
4457 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceFirstSituationDesc").'<br><br>'.$langs->trans("YouMustCreateInvoiceFromThird"), 1, 'help', 'nowraponall', 0, 3, 'firstsituationonsmartphone');
4458 print $desc;
4459 print '</div></div>'."\n";
4460 }
4461
4462 print '<div class="listofinvoicetype"><div class="">';
4463 $tmp = '<input type="radio" name="type" id="radio_replacement" value="0" disabled> ';
4464 $text = $tmp.'<label for="radio_replacement" class="opacitymedium">'.$langs->trans("InvoiceReplacement").'</label> ';
4465 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc").'<br><br>'.$langs->trans("YouMustCreateInvoiceFromThird"), 1, 'help', 'nowraponall', 0, 3, 'replacementonsmartphone');
4466 print $desc;
4467 print '</div></div>'."\n";
4468 }
4469
4470 if (empty($origin)) {
4471 if ($socid > 0) {
4472 // Credit note
4473 if (!getDolGlobalString('INVOICE_DISABLE_CREDIT_NOTE')) {
4474 // Show link for credit note
4475 $facids = $facturestatic->list_qualified_avoir_invoices($soc->id);
4476 if ($facids < 0) {
4477 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
4478 exit;
4479 }
4480 $optionsav = "";
4481 $newinvoice_static = new Facture($db);
4482 foreach ($facids as $key => $valarray) {
4483 $newinvoice_static->id = $key;
4484 $newinvoice_static->ref = $valarray ['ref'];
4485 $newinvoice_static->statut = $valarray ['status'];
4486 $newinvoice_static->status = $valarray ['status'];
4487 $newinvoice_static->type = $valarray ['type'];
4488 $newinvoice_static->paye = $valarray ['paye'];
4489 $newinvoice_static->paid = $valarray ['paye'];
4490 $newinvoice_static->multicurrency_code = (string) $valarray['multicurrency_code'];
4491
4492 $optionsav .= '<option value="'.$key.'"';
4493 if ($key == GETPOST('fac_avoir')) {
4494 $optionsav .= ' selected';
4495
4496 // pre-filled extra fields with selected credit note
4497 $newinvoice_static->fetch_optionals($key);
4498 $object->array_options = $newinvoice_static->array_options;
4499
4500 // Currency is the same of origin (credit note)
4501 $currency_code = $newinvoice_static->multicurrency_code;
4502 }
4503 $optionsav .= '>';
4504 $optionsav .= $newinvoice_static->ref;
4505 $optionsav .= ' ('.$newinvoice_static->getLibStatut(1, $valarray ['paymentornot']).')';
4506 $optionsav .= '</option>';
4507 }
4508
4509 print '<div class="listofinvoicetype"><div class="">';
4510 $tmp = '<input type="radio" id="radio_creditnote" name="type" value="2"'.(GETPOST('type') == 2 ? ' checked' : '');
4511 if ((!$optionsav && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) || $invoice_predefined->id > 0) {
4512 $tmp .= ' disabled';
4513 }
4514 $tmp .= '> ';
4515 // Show credit note options only if we checked credit note and disable standard invoice if "create credit note" button is pressed
4516 print '<script type="text/javascript">
4517 jQuery(document).ready(function() {
4518 if (jQuery("#radio_creditnote").is(":checked")) {
4519 jQuery("#radio_standard").prop("disabled", true);
4520 jQuery("#radio_deposit").prop("disabled", true);
4521 } else {
4522 jQuery("#radio_standard").prop("disabled", false);
4523 jQuery("#radio_deposit").prop("disabled", false);
4524 }
4525 if (! jQuery("#radio_creditnote").is(":checked")) {
4526 jQuery("#credit_note_options").hide();
4527 }
4528 jQuery("#radio_creditnote").click(function() {
4529 jQuery("#credit_note_options").show();
4530 });
4531 jQuery("#radio_standard, #radio_replacement, #radio_deposit, #radio_situation, #radio_situation_bis").click(function() {
4532 console.log("We click on a radio to close credit not options");
4533 jQuery("#credit_note_options").hide();
4534 });
4535 });
4536 </script>';
4537 $text = '<label>'.$tmp.$langs->transnoentities("InvoiceAvoirAsk").'</label> ';
4538 $text .= '<select class="flat valignmiddle minwidth200" name="fac_avoir" id="fac_avoir"';
4539 if (!$optionsav || $invoice_predefined->id > 0) {
4540 $text .= ' disabled';
4541 }
4542 $text .= '>';
4543 if ($optionsav) {
4544 $text .= '<option value="-1">'.$langs->trans("InvoiceAvoirAskCombo").'</option>';
4545 $text .= $optionsav;
4546 } else {
4547 $text .= '<option value="-1">'.$langs->trans("NoInvoiceToCorrect").'</option>';
4548 }
4549 $text .= '</select>';
4550 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3);
4551 print $desc;
4552
4553 print '<div id="credit_note_options" class="clearboth paddingtop marginbottomonly">';
4554 print '<div class="marginleftlargeondesktop"><input type="checkbox" name="invoiceAvoirWithLines" id="invoiceAvoirWithLines" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithLines') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithLines" class="small">'.$langs->trans('invoiceAvoirWithLines')."</label></div>";
4555 print '<div class="marginleftlargeondesktop"><input type="checkbox" name="invoiceAvoirWithPaymentRestAmount" id="invoiceAvoirWithPaymentRestAmount" value="1" onclick="$(\'#credit_note_options input[type=checkbox]\').not(this).prop(\'checked\', false);" '.(GETPOSTINT('invoiceAvoirWithPaymentRestAmount') > 0 ? 'checked' : '').' /> <label for="invoiceAvoirWithPaymentRestAmount" class="small">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label></div>";
4556 // Adding a checkbox: "Automatically consume the credit note to close the corrected invoice" is better to be into
4557 // the confirm popup when we validate the credit note
4558 print '</div>';
4559
4560 print '</div></div>'."\n";
4561 }
4562 } else {
4563 print '<div class="listofinvoicetype"><div class="">';
4564 if (!getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
4565 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="0" disabled> ';
4566 } else {
4567 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="2" > ';
4568 }
4569 $text = $tmp.'<label class="opacitymedium" for="radio_creditnote">'.$langs->trans("InvoiceAvoir").'</label> ';
4570 //$text .= '<span class="opacitymedium hideonsmartphone">('.$langs->trans("YouMustCreateInvoiceFromThird").')</span> ';
4571 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc").'<br><br>'.$langs->trans("CreateCreditNoteWhenClientInvoiceExists"), 1, 'help', '', 0, 3, 'creditnoteonsmartphone');
4572 print $desc;
4573 print '</div></div>'."\n";
4574 }
4575 }
4576
4577 // Template invoice
4578 /*
4579 print '<div class="listofinvoicetype"><div class="">';
4580 $tmp = '<input type="radio" name="type" id="radio_template" value="0" disabled> ';
4581 $text = $tmp.'<label class="opacitymedium" for="radio_template">'.$langs->trans("RepeatableInvoice").'</label> ';
4582 $desc = $form->textwithpicto($text, $langs->transnoentities("YouMustCreateStandardInvoiceFirstDesc"), 1, 'help', '', 0, 3, 'templateonsmartphone');
4583 print $desc;
4584 print '</div></div>'."\n";
4585 */
4586
4587 print '</div><br>';
4588
4589
4590 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) { // Hidden conf
4591 // Add auto select default document model
4593 $jsListType = '';
4594 foreach ($listtType as $type) {
4595 $thisTypeConfName = 'FACTURE_ADDON_PDF_'.$type;
4596 $current = getDolGlobalString($thisTypeConfName, getDolGlobalString('FACTURE_ADDON_PDF'));
4597 $jsListType .= (!empty($jsListType) ? ',' : '').'"'.$type.'":"'.$current.'"';
4598 }
4599
4600 print '<script type="text/javascript">
4601 $(document).ready(function() {
4602 var listType = {'.$jsListType.'};
4603 $("[name=\'type\']").change(function() {
4604 console.log("change name=type");
4605 if ($( this ).prop("checked"))
4606 {
4607 if(($( this ).val() in listType))
4608 {
4609 $("#model").val(listType[$( this ).val()]).trigger("change");
4610 }
4611 else
4612 {
4613 $("#model").val("' . getDolGlobalString('FACTURE_ADDON_PDF').'").trigger("change");
4614 }
4615 }
4616 });
4617 });
4618 </script>';
4619 }
4620
4621
4622 print '</td></tr>';
4623
4624 // Invoice Subtype
4625 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED')) {
4626 print '<tr><td class="fieldrequired">'.$langs->trans('InvoiceSubtype').'</td><td colspan="2">';
4627 print $form->getSelectInvoiceSubtype(GETPOSTINT('subtype'), 'subtype', 1, 0, '');
4628 print '</td></tr>';
4629 }
4630
4631 // Discounts for the known third party
4632 if ($socid > 0) {
4633 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td><td colspan="2">';
4634
4635 $thirdparty = $soc; // used by object_discounts.tpl.php
4636 $discount_type = 0; // used by object_discounts.tpl.php
4637 $backtopage = $_SERVER["PHP_SELF"].'?socid='.$thirdparty->id.'&action='.$action.'&origin='.urlencode((string) (GETPOST('origin'))).'&originid='.urlencode((string) (GETPOSTINT('originid')));
4638
4639 // loading object_discounts.tpl.php from module core/tpl if exists
4640 $defaulttpldir = '/core/tpl';
4641 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4642 foreach ($dirtpls as $module => $reldir) {
4643 $res = 0;
4644 if (!empty($module)) {
4645 $tpl = dol_buildpath($reldir.'/object_discounts.tpl.php');
4646 } else {
4647 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/object_discounts.tpl.php';
4648 }
4649 if (file_exists($tpl)) {
4650 if (empty($conf->file->strict_mode)) {
4651 $res = @include $tpl;
4652 } else {
4653 $res = include $tpl;
4654 }
4655 }
4656 if ($res) {
4657 break;
4658 }
4659 }
4660
4661 print '</td></tr>';
4662 }
4663
4664 $newdateinvoice = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
4665 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
4666
4667 // Date invoice
4668 print '<tr><td class="fieldrequired">'.$langs->trans('DateInvoice').'</td><td colspan="2">';
4669 print img_picto('', 'action', 'class="pictofixedwidth"');
4670 print $form->selectDate($newdateinvoice ? $newdateinvoice : $dateinvoice, '', 0, 0, 0, "add", 1, 1);
4671 print '</td></tr>';
4672
4673 // Date point of tax
4674 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
4675 print '<tr><td class="fieldrequired">'.$langs->trans('DatePointOfTax').'</td><td colspan="2">';
4676 print img_picto('', 'action', 'class="pictofixedwidth"');
4677 print $form->selectDate($date_pointoftax ? $date_pointoftax : -1, 'date_pointoftax', 0, 0, 0, "add", 1, 1);
4678 print '</td></tr>';
4679 }
4680
4681 // Payment term
4682 print '<tr><td class="nowrap fieldrequired">'.$langs->trans('PaymentConditionsShort').'</td><td colspan="2">';
4683 print img_picto('', 'payment', 'class="pictofixedwidth"');
4684 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', -1, 1, 0, 'maxwidth500 widthcentpercentminusx');
4685 print '</td></tr>';
4686
4687 // Retained warranty
4688 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
4689 $rwStyle = 'display:none;';
4690 if (in_array(GETPOSTINT('type'), $retainedWarrantyInvoiceAvailableType)) {
4691 $rwStyle = '';
4692 }
4693
4694 $retained_warranty = GETPOSTINT('retained_warranty');
4695 if (empty($retained_warranty)) {
4696 if ($objectsrc !== null && property_exists($objectsrc, 'retained_warranty') && !empty($objectsrc->retained_warranty)) { // use previous situation value
4697 // Facture->retained_warranty (does not exist on Expedition)
4698 $retained_warranty = $objectsrc->retained_warranty; // @phan-suppress-current-line PhanUndeclaredProperty
4699 }
4700 }
4701 $retained_warranty_js_default = !empty($retained_warranty) ? $retained_warranty : getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_PERCENT');
4702
4703 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('RetainedWarranty').'</td><td colspan="2">';
4704 print '<input id="new-situation-invoice-retained-warranty" name="retained_warranty" type="number" value="'.$retained_warranty.'" step="0.01" min="0" max="100" />%';
4705
4706 // Retained warranty payment term
4707 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('PaymentConditionsShortRetainedWarranty').'</td><td colspan="2">';
4708 $retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
4709 if (empty($retained_warranty_fk_cond_reglement)) {
4710 $retained_warranty_fk_cond_reglement = getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
4711 if ($objectsrc instanceOf Facture && !empty($objectsrc->retained_warranty_fk_cond_reglement)) { // use previous situation value
4712 // Facture->retained_warranty_fk_cond_reglement (does not exist on Expedition)
4713 $retained_warranty_fk_cond_reglement = $objectsrc->retained_warranty_fk_cond_reglement; // @phan-suppress-current-line PhanUndeclaredProperty
4714 }
4715 }
4716 print $form->getSelectConditionsPaiements($retained_warranty_fk_cond_reglement, 'retained_warranty_fk_cond_reglement', -1, 1);
4717 print '</td></tr>';
4718
4719 print '<script type="text/javascript">
4720 $(document).ready(function() {
4721 $("[name=\'type\']").change(function() {
4722 if($( this ).prop("checked") && $.inArray($( this ).val(), '.json_encode($retainedWarrantyInvoiceAvailableType).' ) !== -1)
4723 {
4724 $(".retained-warranty-line").show();
4725 $("#new-situation-invoice-retained-warranty").val("'.(float) $retained_warranty_js_default.'");
4726 }
4727 else{
4728 $(".retained-warranty-line").hide();
4729 $("#new-situation-invoice-retained-warranty").val("");
4730 }
4731 });
4732
4733 $("[name=\'type\']:checked").trigger("change");
4734 });
4735 </script>';
4736 }
4737
4738 // Payment mode
4739 print '<tr><td>'.$langs->trans('PaymentMode').'</td><td colspan="2">';
4740 print img_picto('', 'bank', 'class="pictofixedwidth"');
4741 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth250 widthcentpercentminusx', 1);
4742 print '</td></tr>';
4743
4744 // Bank Account
4745 if (isModEnabled("bank")) {
4746 print '<tr><td>'.$langs->trans('DefaultBankAccount').'</td><td colspan="2">';
4747 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4748 print $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth250 widthcentpercentminusx', 1);
4749 print '</td></tr>';
4750 }
4751
4752 // Source / Channel - What trigger creation
4753 if (!getDolGlobalInt('INVOICE_DISABLE_SOURCE')) {
4754 print '<tr><td>'.$langs->trans('Source').'</td><td>';
4755 print img_picto('', 'question', 'class="pictofixedwidth"');
4756 $form->selectInputReason((string) $inputReasonId, 'input_reason_id', '', 1, 'maxwidth250 widthcentpercentminusx');
4757 print '</td></tr>';
4758 } else {
4759 print '<input type="hidden" name="input_reason_id" value="'.((string) $inputReasonId).'">';
4760 }
4761
4762 // Project
4763 if (isModEnabled('project') && is_object($formproject)) {
4764 $langs->load('projects');
4765 print '<tr><td>'.$langs->trans('Project').'</td><td colspan="2">';
4766 print img_picto('', 'project', 'class="pictofixedwidth"');
4767
4768 print $formproject->select_projects((($socid > 0 && !getDolGlobalString('PROJECT_CAN_ALWAYS_LINK_TO_ALL_CUSTOMERS')) ? $socid : -1), (string) $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500 widthcentpercentminusxx');
4769 print ' <a href="'.DOL_URL_ROOT.'/projet/card.php?socid='.$soc->id.'&action=create&status=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create&socid='.$soc->id.($fac_rec ? '&fac_rec='.$fac_rec : '')).'"><span class="fa fa-plus-circle valignmiddle" title="'.$langs->trans("AddProject").'"></span></a>';
4770 print '</td></tr>';
4771 }
4772
4773 // Incoterms
4774 if (isModEnabled('incoterm')) {
4775 print '<tr>';
4776 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), !empty($objectsrc->label_incoterms) ? $objectsrc->label_incoterms : '', 1).'</label></td>';
4777 print '<td colspan="2" class="maxwidthonsmartphone">';
4778 $incoterm_id = GETPOST('incoterm_id');
4779 $location_incoterms = GETPOST('location_incoterms');
4780 if (empty($incoterm_id)) {
4781 $incoterm_id = (!empty($objectsrc->fk_incoterms) ? $objectsrc->fk_incoterms : $soc->fk_incoterms);
4782 $location_incoterms = (!empty($objectsrc->location_incoterms) ? $objectsrc->location_incoterms : $soc->location_incoterms);
4783 }
4784 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
4785 print $form->select_incoterms($incoterm_id, $location_incoterms);
4786 print '</td></tr>';
4787 }
4788
4789 // Dispute
4790 /* Not necessary on creation
4791 print '<tr><td class="nowrap fieldrequired">'.$langs->trans('Dispute').'</td><td colspan="2">';
4792 //print yn($object->dispute_status);
4793 print '</td></tr>';
4794 */
4795
4796 // Category
4797 if (isModEnabled('category')) {
4798 // Categories
4799 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
4800 print $form->selectCategories(Categorie::TYPE_INVOICE, 'categories', $object);
4801 print "</td></tr>";
4802 }
4803
4804 // Other attributes
4805 $parameters = array('objectsrc' => !empty($objectsrc) ? $objectsrc : 0, 'colspan' => ' colspan="2"', 'cols' => '2', 'socid' => $socid);
4806 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
4807 print $hookmanager->resPrint;
4808 if (empty($reshook)) {
4809 if (getDolGlobalString('THIRDPARTY_PROPAGATE_EXTRAFIELDS_TO_INVOICE') && !empty($soc->id)) {
4810 // copy from thirdparty
4811 $tpExtrafields = new ExtraFields($db);
4812 $tpExtrafieldLabels = $tpExtrafields->fetch_name_optionals_label($soc->table_element);
4813 if ($soc->fetch_optionals() > 0) {
4814 $object->array_options = array_merge($object->array_options, $soc->array_options);
4815 }
4816 }
4817
4818 print $object->showOptionals($extrafields, 'create', $parameters);
4819 }
4820
4821 // Template to use by default
4822 include_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
4824 if (is_array($list) && count($list) > 0) {
4825 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) {
4826 $type = GETPOSTISSET('type') ? GETPOSTINT('type') : $object->type;
4827 // Hidden conf
4828 $paramkey = 'FACTURE_ADDON_PDF_'.$type;
4829 $preselected = getDolGlobalString($paramkey, getDolGlobalString('FACTURE_ADDON_PDF'));
4830 } else {
4831 $preselected = getDolGlobalString('FACTURE_ADDON_PDF');
4832 }
4833 if (count($list) > 1) {
4834 print '<tr><td>'.$langs->trans('Model').'</td>';
4835 print '<td colspan="2">';
4836 print img_picto('', 'pdf', 'class="pictofixedwidth"');
4837 print $form->selectarray('model', $list, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
4838 print "</td></tr>";
4839 } else {
4840 print '<input type="hidden" name="model" value="'.$preselected.'">';
4841 }
4842 }
4843
4844 // Multicurrency
4845 if (isModEnabled('multicurrency')) {
4846 print '<tr>';
4847 print '<td>'.$form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0).'</td>';
4848 print '<td colspan="2" class="maxwidthonsmartphone">';
4849 print img_picto('', 'currency', 'class="pictofixedwidth"');
4850 print $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
4851 print '</td></tr>';
4852 }
4853
4854 // Help of substitution key
4855 $htmltext = '';
4856 if (GETPOSTINT('fac_rec') > 0) {
4857 $dateexample = ($newdateinvoice ? $newdateinvoice : $dateinvoice);
4858 if (empty($dateexample)) {
4859 $dateexample = dol_now();
4860 }
4861 $substitutionarray = array(
4862 '__TOTAL_HT__' => $langs->trans("AmountHT").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ht).')',
4863 '__TOTAL_TTC__' => $langs->trans("AmountTTC").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ttc).')',
4864 '__INVOICE_PREVIOUS_MONTH__' => $langs->trans("PreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%m').')',
4865 '__INVOICE_MONTH__' => $langs->trans("MonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%m').')',
4866 '__INVOICE_NEXT_MONTH__' => $langs->trans("NextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%m').')',
4867 '__INVOICE_PREVIOUS_MONTH_TEXT__' => $langs->trans("TextPreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%B').')',
4868 '__INVOICE_MONTH_TEXT__' => $langs->trans("TextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%B').')',
4869 '__INVOICE_NEXT_MONTH_TEXT__' => $langs->trans("TextNextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%B').')',
4870 '__INVOICE_PREVIOUS_YEAR__' => $langs->trans("PreviousYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'y'), '%Y').')',
4871 '__INVOICE_YEAR__' => $langs->trans("YearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%Y').')',
4872 '__INVOICE_NEXT_YEAR__' => $langs->trans("NextYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'y'), '%Y').')'
4873 );
4874
4875 $htmltext = '<i>'.$langs->trans("FollowingConstantsWillBeSubstituted").':<br>';
4876 foreach ($substitutionarray as $key => $val) {
4877 $htmltext .= $key.' = '.$langs->trans($val).'<br>';
4878 }
4879 $htmltext .= '</i>';
4880 }
4881
4882 // Public note
4883 print '<tr>';
4884 print '<td class="tdtop">';
4885 print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
4886 print '</td>';
4887 print '<td valign="top" colspan="2">';
4888 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
4889 print $doleditor->Create(1);
4890
4891 // Private note
4892 if (empty($user->socid)) {
4893 print '<tr>';
4894 print '<td class="tdtop">';
4895 print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
4896 print '</td>';
4897 print '<td valign="top" colspan="2">';
4898 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
4899 print $doleditor->Create(1);
4900 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
4901 print '</td></tr>';
4902 }
4903
4904 // Lines from source (TODO Show them also when creating invoice from template invoice)
4905 if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($classname)) {
4906 $langs->loadLangs(array('orders', 'propal'));
4907
4908 // TODO for compatibility
4909 if ($origin == 'contrat') {
4910 '@phan-var-force Contrat $objectsrc';
4911 // Recalculate contrat->price (excl. VAT), contrat->total (incl. VAT), contrat->tva
4912 $objectsrc->update_price(1, 'auto', 1);
4913 }
4914
4915 print "\n<!-- Show ref of origin ".$classname." -->\n";
4916 print '<input type="hidden" name="amount" value="'.$objectsrc->total_ht.'">'."\n";
4917 print '<input type="hidden" name="total" value="'.$objectsrc->total_ttc.'">'."\n";
4918 print '<input type="hidden" name="tva" value="'.$objectsrc->total_tva.'">'."\n";
4919 // The commented lines below are fields already added as hidden parameters before
4920 //print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
4921 //print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
4922
4923 switch (get_class($objectsrc)) {
4924 case 'Propal':
4925 $newclassname = 'CommercialProposal';
4926 break;
4927 case 'Commande':
4928 $newclassname = 'Order';
4929 break;
4930 case 'Expedition':
4931 $newclassname = 'Sending';
4932 break;
4933 case 'Contrat':
4934 $newclassname = 'Contract';
4935 break;
4936 case 'Fichinter':
4937 $newclassname = 'Intervention';
4938 break;
4939 default:
4940 $newclassname = get_class($objectsrc);
4941 }
4942
4943 // Ref of origin
4944 print '<tr><td>'.$langs->trans($newclassname).'</td>';
4945 print '<td colspan="2">';
4946 print $objectsrc->getNomUrl(1);
4947 // We check if Origin document (id and type is known) has already at least one invoice attached to it
4948 $objectsrc->fetchObjectLinked($originid, $origin, null, 'facture');
4949 if (isset($objectsrc->linkedObjects['facture']) && is_array($objectsrc->linkedObjects['facture']) && count($objectsrc->linkedObjects['facture']) >= 1) {
4950 setEventMessages('WarningBillExist', null, 'warnings');
4951 echo ' - '.$langs->trans('LatestRelatedBill').' '.end($objectsrc->linkedObjects['facture'])->getNomUrl(1);
4952 }
4953 echo '</td></tr>';
4954
4955 print '<tr><td>'.$langs->trans('AmountHT').'</td><td colspan="2">'.price($objectsrc->total_ht, 1, $langs, 1, -1, '', $conf->currency).'</td></tr>';
4956 print '<tr><td>'.$langs->trans('AmountVAT').'</td><td colspan="2">'.price($objectsrc->total_tva, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4957 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
4958 print '<tr><td>'.$langs->transcountry("AmountLT1", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax1, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4959 }
4960
4961 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
4962 print '<tr><td>'.$langs->transcountry("AmountLT2", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax2, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4963 }
4964 print '<tr><td>'.$langs->trans('AmountTTC').'</td><td colspan="2">'.price($objectsrc->total_ttc, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4965
4966 if (isModEnabled('multicurrency') && $objectsrc->multicurrency_code != $conf->currency) {
4967 //var_dump($objectsrc);
4968 print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ht, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code).'</td></tr>';
4969 print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_tva, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
4970 print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ttc, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
4971 }
4972 }
4973
4974 print "</table>\n";
4975 }
4976 print dol_get_fiche_end();
4977
4978 print $form->buttonsSaveCancel("CreateDraft");
4979
4980 // Show origin lines
4981 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
4982 print '<br>';
4983
4984 $title = $langs->trans('ProductsAndServices');
4985 print load_fiche_titre($title);
4986
4987 print '<div class="div-table-responsive-no-min">';
4988 print '<table class="noborder centpercent">';
4989
4990 $objectsrc->printOriginLinesList('', $selectedLines);
4991
4992 print '</table>';
4993 print '</div>';
4994 }
4995
4996 print "</form>\n";
4997} elseif ($id > 0 || !empty($ref)) {
4998 if (empty($object->id)) {
4999 $langs->load('errors');
5000 echo '<div class="error">'.$langs->trans("ErrorRecordNotFound").'</div>';
5001 llxFooter();
5002 exit;
5003 }
5004
5005 /*
5006 * Show object in view mode
5007 */
5008
5009 if ($user->socid > 0 && $user->socid != $object->socid) {
5010 accessforbidden('', 0, 1);
5011 }
5012
5013 $result = $object->fetch_thirdparty();
5014
5015 $result = $soc->fetch($object->socid);
5016 if ($result < 0) {
5018 }
5019 $selleruserevenustamp = $mysoc->useRevenueStamp();
5020
5021 $totalpaid = $object->getSommePaiement();
5022 $totalcreditnotes = $object->getSumCreditNotesUsed();
5023 $totaldeposits = $object->getSumDepositsUsed();
5024 //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits."
5025 // selleruserrevenuestamp=".$selleruserevenustamp;
5026
5027 // We can also use bcadd to avoid pb with floating points
5028 // For example print 239.2 - 229.3 - 9.9; does not return 0.
5029 $resteapayer = price2num($object->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
5030
5031 // Multicurrency
5032 if (isModEnabled('multicurrency')) {
5033 $multicurrency_totalpaid = $object->getSommePaiement(1);
5034 $multicurrency_totalcreditnotes = $object->getSumCreditNotesUsed(1);
5035 $multicurrency_totaldeposits = $object->getSumDepositsUsed(1);
5036 $multicurrency_resteapayer = price2num($object->multicurrency_total_ttc - $multicurrency_totalpaid - $multicurrency_totalcreditnotes - $multicurrency_totaldeposits, 'MT');
5037 // Code to fix case of corrupted data
5038 // TODO We should not need this. Also data comes from a not reliable value of $object->multicurrency_total_ttc that may be wrong if it was
5039 // calculated by summing lines that were in a currency for some of them and into another for others (lines from discount/down payment into another currency for example)
5040 if ($resteapayer == 0 && $multicurrency_resteapayer != 0 && $object->multicurrency_code != $conf->currency) {
5041 $resteapayer = price2num((float) $multicurrency_resteapayer / $object->multicurrency_tx, 'MT');
5042 }
5043 }
5044
5045 if ($object->paye || $object->status == $object::STATUS_CLOSED) {
5046 $resteapayer = 0;
5047 }
5048 $resteapayeraffiche = $resteapayer;
5049
5050 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) { // Never use this
5051 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
5052 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
5053 } else {
5054 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
5055 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
5056 }
5057
5058 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
5059 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
5060 $absolute_discount = price2num($absolute_discount, 'MT');
5061 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
5062
5063 $nb_creditnote_notyetavailable = $soc->getOpenCreditNotesNotYetConvertedIntoDiscount(0);
5064
5065 $author = new User($db);
5066 if ($object->user_creation_id) {
5067 $author->fetch($object->user_creation_id);
5068 }
5069
5070 $objectidnext = $object->getIdReplacingInvoice();
5071
5072 $head = facture_prepare_head($object);
5073
5074 print dol_get_fiche_head($head, 'compta', $langs->trans('InvoiceCustomer'), -1, $object->picto, 0, '', '', 0, '', 1);
5075
5076 $formconfirm = '';
5077
5078 // Confirmation of the conversion of the credit into a reduction
5079 if ($action == 'converttoreduc') {
5081 $type_fac = 'ExcessReceived';
5082 } elseif ($object->type == Facture::TYPE_CREDIT_NOTE) {
5083 $type_fac = 'CreditNote';
5084 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
5085 $type_fac = 'Deposit';
5086 } else {
5087 $type_fac = '';
5088 }
5089 $text = $langs->trans('ConfirmConvertToReduc', strtolower($langs->transnoentities($type_fac)));
5090 $text .= '<br>'.$langs->trans('ConfirmConvertToReduc2');
5091 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('ConvertToReduc'), $text, 'confirm_converttoreduc', '', "yes", 2);
5092 }
5093
5094 // Confirmation to delete invoice
5095 if ($action == 'delete') {
5096 $text = $langs->trans('ConfirmDeleteBill', $object->ref);
5097 $formquestion = array();
5098
5099 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL') && $object->status >= 1) {
5100 $qualified_for_stock_change = 0;
5101 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
5102 $qualified_for_stock_change = $object->hasProductsOrServices(2);
5103 } else {
5104 $qualified_for_stock_change = $object->hasProductsOrServices(1);
5105 }
5106
5107 if ($qualified_for_stock_change) {
5108 $langs->load("stocks");
5109 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
5110 $formproduct = new FormProduct($db);
5111 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
5112 $forcecombo = 0;
5113 if ($conf->browser->name == 'ie') {
5114 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
5115 }
5116 $formquestion = array(
5117 // 'text' => $langs->trans("ConfirmClone"),
5118 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
5119 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
5120 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1, 0, 0, $langs->trans("NoStockAction"), 0, $forcecombo))
5121 );
5122 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', $formquestion, "yes", 1);
5123 } else {
5124 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
5125 }
5126 } else {
5127 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
5128 }
5129 }
5130
5131 // Confirmation to remove invoice from cycle
5132 if ($action == 'situationout') {
5133 $text = $langs->trans('ConfirmRemoveSituationFromCycle', $object->ref);
5134 $label = $langs->trans("ConfirmOuting");
5135 $formquestion = array();
5136 // remove situation from cycle
5137 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
5138 && $usercancreate
5139 && !$objectidnext
5140 && $object->is_last_in_cycle()
5141 && $usercanunvalidate
5142 ) {
5143 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $label, $text, 'confirm_situationout', $formquestion, "yes", 1);
5144 }
5145 }
5146
5147 // Confirmation of validation
5148 if ($action == 'valid') {
5149 // we check object has a draft number
5150 $objectref = substr($object->ref, 1, 4);
5151 if ($objectref == 'PROV') {
5152 //$savdate = $object->date;
5153 if (getDolGlobalString('FAC_FORCE_DATE_VALIDATION')) {
5154 $object->date = dol_now();
5155 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
5156 }
5157 $numref = $object->getNextNumRef($soc);
5158 // $object->date=$savdate;
5159 } else {
5160 $numref = (string) $object->ref;
5161 }
5162
5163 $text = $langs->trans('ConfirmValidateBill', $numref);
5164 if (getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
5165 $text .= '<br><br>';
5166 $text .= img_picto('', 'warning', 'class="pictofixedwidth"').' '.$langs->trans('WarningInvoiceCanNeverBeEdited');
5167 }
5168 if (isModEnabled('notification')) {
5169 require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
5170 $notify = new Notify($db);
5171 $text .= '<br>';
5172 $text .= $notify->confirmMessage('BILL_VALIDATE', $object->socid, $object);
5173 }
5174 $formquestion = array();
5175
5176 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
5177 $qualified_for_stock_change = 0;
5178 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
5179 $qualified_for_stock_change = $object->hasProductsOrServices(2);
5180 } else {
5181 $qualified_for_stock_change = $object->hasProductsOrServices(1);
5182 }
5183
5184 if ($qualified_for_stock_change) {
5185 $langs->load("stocks");
5186 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
5187 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
5188 $formproduct = new FormProduct($db);
5189 $warehouse = new Entrepot($db);
5190 $warehouse_array = $warehouse->list_array();
5191 if (count($warehouse_array) == 1) {
5192 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockIncrease", current($warehouse_array)) : $langs->trans("WarehouseForStockDecrease", current($warehouse_array));
5193 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
5194 } else {
5195 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockIncrease") : $langs->trans("SelectWarehouseForStockDecrease");
5196 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
5197 }
5198 $formquestion = array(
5199 // 'text' => $langs->trans("ConfirmClone"),
5200 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
5201 // 1),
5202 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
5203 // => 1),
5204 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
5205 }
5206 }
5207 if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) { // Can happen only if getDolGlobalString('FACTURE_ENABLE_NEGATIVE') is on
5208 $text .= '<br>'.img_warning().' '.$langs->trans("ErrorInvoiceOfThisTypeMustBePositive");
5209 }
5210
5211 // mandatoryPeriod
5212 $nbMandated = 0;
5213 foreach ($object->lines as $line) {
5214 $res = $line->fetch_product();
5215 if ($res > 0) {
5216 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
5217 $nbMandated++;
5218 break;
5219 }
5220 }
5221 }
5222 if ($nbMandated > 0) {
5223 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
5224 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
5225 $error++;
5226 } else {
5227 $text .= '<div><span class="clearboth nowraponall warning">'.img_warning().$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
5228 }
5229 }
5230
5231 if (!$error) {
5232 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ValidateBill'), $text, 'confirm_valid', $formquestion, (($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) ? "no" : "yes"), 2, 260);
5233 }
5234 }
5235
5236 // Confirm back to draft status (action = 'modif')
5237 if ($action == 'modif') {
5238 $oktomodif = 1; // Assume we can modify by default
5239
5240 $testvalue = $object->isEditable();
5241 if ($testvalue < 0) {
5242 switch ($testvalue) {
5243 case -1:
5244 // Dispatched in bookkeeping
5245 setEventMessages($langs->trans("DisabledBecauseDispatchedInBookkeeping"), null, 'errors');
5246 break;
5247 case -2:
5248 // Not last invoice
5249 setEventMessages($langs->trans("DisabledBecauseNotLastInvoice"), null, 'errors');
5250 break;
5251 case -3:
5252 // Not last situation invoice
5253 setEventMessages($langs->trans("DisabledBecauseNotLastSituationInvoice"), null, 'errors');
5254 break;
5255 case -4:
5256 // At least one payment made
5257 setEventMessages($langs->trans("DisabledBecauseThereIsAPayment"), null, 'errors');
5258 break;
5259 case -5:
5260 // Already sent by email
5261 setEventMessages($langs->trans("DisabledBecauseAlreadySentByEmail"), null, 'errors');
5262 break;
5263 case -6:
5264 // Already printed once
5265 setEventMessages($langs->trans("DisabledBecauseAlreadyPrintedOnce"), null, 'errors');
5266 break;
5267 case -7:
5268 // Already validated
5269 setEventMessages($langs->trans("DisabledBecauseVersionProtected").(empty($object->error) ? '' : ': '.$object->error), null, 'errors');
5270 break;
5271 default:
5272 // Other error
5273 setEventMessages($langs->trans("DisabledBecauseNotEditable").(empty($object->error) ? ': UnknownReason' : ': '.$object->error), null, 'errors');
5274 break;
5275 }
5276 $oktomodif = 0;
5277 $action = '';
5278 }
5279
5280 if ($oktomodif) {
5281 $text = $langs->trans('ConfirmUnvalidateBill', $object->ref);
5282 $formquestion = array();
5283
5284 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
5285 $qualified_for_stock_change = 0;
5286 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
5287 $qualified_for_stock_change = $object->hasProductsOrServices(2);
5288 } else {
5289 $qualified_for_stock_change = $object->hasProductsOrServices(1);
5290 }
5291
5292 if ($qualified_for_stock_change) {
5293 $langs->load("stocks");
5294 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
5295 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
5296 $formproduct = new FormProduct($db);
5297 $warehouse = new Entrepot($db);
5298 $warehouse_array = $warehouse->list_array();
5299 if (count($warehouse_array) == 1) {
5300 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockDecrease", current($warehouse_array)) : $langs->trans("WarehouseForStockIncrease", current($warehouse_array));
5301 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
5302 } else {
5303 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
5304 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
5305 }
5306 $formquestion = array(
5307 // 'text' => $langs->trans("ConfirmClone"),
5308 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
5309 // 1),
5310 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
5311 // => 1),
5312 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
5313 }
5314 }
5315
5316 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('UnvalidateBill'), $text, 'confirm_modif', $formquestion, "yes", 1);
5317 }
5318 }
5319
5320 // Confirmation of status abandoned (when no payment never done)
5321 if ($action == 'canceled') {
5322 // If there is a replacement invoice not yet validated (draft state),
5323 // it is not allowed to classify the invoice as abandoned.
5324
5325 $statusreplacement = 0;
5326
5327 if ($objectidnext) {
5328 $facturereplacement = new Facture($db);
5329 $facturereplacement->fetch($objectidnext);
5330 $statusreplacement = $facturereplacement->status;
5331 }
5332 if ($objectidnext && $statusreplacement == 0) {
5333 print '<div class="error">'.$langs->trans("ErrorCantCancelIfReplacementInvoiceNotValidated").'</div>';
5334 } else {
5335 $close = array();
5336 // Code
5337 $close[1]['code'] = 'badcustomer';
5338 $close[2]['code'] = 'abandon';
5339 // Help
5340 $close[1]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
5341 $close[2]['label'] = $langs->trans("ConfirmClassifyAbandonReasonOtherDesc");
5342 // Text
5343 $close[1]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $object->ref), $close[1]['label'], 1);
5344 $close[2]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyAbandonReasonOther"), $close[2]['label'], 1);
5345 // arrayreasons
5346 $arrayreasons = [];
5347 $arrayreasons[$close[1]['code']] = $close[1]['reason'];
5348 $arrayreasons[$close[2]['code']] = $close[2]['reason'];
5349
5350 // Create a form table
5351 $formquestion = array('text' => $langs->trans("ConfirmCancelBillQuestion"), 0 => array('type' => 'radio', 'name' => 'close_code', 'label' => $langs->trans("Reason"), 'values' => $arrayreasons), 1 => array('type' => 'text', 'name' => 'close_note', 'label' => $langs->trans("Comment"), 'value' => '', 'morecss' => 'minwidth300'));
5352
5353 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('CancelBill'), $langs->trans('ConfirmCancelBill', $object->ref), 'confirm_canceled', $formquestion, "yes", 1, 300);
5354 }
5355 }
5356
5357 // Confirmation of payment classification (when some payment started)
5358 if ($action == 'paid' && ($resteapayer <= 0 || (getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') && $resteapayer == $object->total_ttc))) {
5359 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidBill', $object->ref), 'confirm_paid', '', "yes", 1);
5360 }
5361 if ($action == 'paid' && $resteapayer > 0 && (!getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') || $resteapayer != $object->total_ttc)) {
5362 $close = array();
5363 // Code
5364 $i = 0;
5365 $close[$i]['code'] = $object::CLOSECODE_DISCOUNTVAT; // escompte
5366 $i++;
5367 $close[$i]['code'] = $object::CLOSECODE_BADDEBT;
5368 $i++;
5369 $close[$i]['code'] = $object::CLOSECODE_BANKCHARGE;
5370 $i++;
5371 $close[$i]['code'] = $object::CLOSECODE_WITHHOLDINGTAX;
5372 $i++;
5373 $close[$i]['code'] = $object::CLOSECODE_OTHER;
5374 $i++;
5375 // Help
5376 $i = 0;
5377 $close[$i]['label'] = $langs->trans("HelpEscompte").'<br><br>'.$langs->trans("ConfirmClassifyPaidPartiallyReasonDiscountVatDesc");
5378 $i++;
5379 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
5380 $i++;
5381 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBankChargeDesc");
5382 $i++;
5383 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonWithholdingTaxDesc");
5384 $i++;
5385 $close[$i]['label'] = $langs->trans("Other");
5386 $i++;
5387 // Texte
5388 $i = 0;
5389 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonDiscount", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5390 $i++;
5391 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5392 $i++;
5393 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBankCharge", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5394 $i++;
5395 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonWithholdingTax"), $close[$i]['label'], 1);
5396 $i++;
5397 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("Other"), $close[$i]['label'], 1);
5398 $i++;
5399 // arrayreasons[code]=reason
5400 $arrayreasons = [];
5401 foreach ($close as $key => $val) {
5402 $arrayreasons[$close[$key]['code']] = '<span class="small">'.$close[$key]['reason'].'</span>';
5403 }
5404
5405 // Create a form table
5406 $formquestion = array(
5407 'text' => $langs->trans("ConfirmClassifyPaidPartiallyQuestion"),
5408 0 => array('type' => 'radio', 'name' => 'close_code', 'label' => '', 'values' => $arrayreasons),
5409 1 => array('type' => 'text', 'name' => 'close_note', 'moreattr' => 'placeholder = "'.$langs->trans("Comment").'"', 'value' => '', 'morecss' => 'minwidth300'),
5410 2 => array('type' => 'separator')
5411 );
5412 // Incomplete payment. We ask if reason = discount or other
5413 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&resteapayer='.((float) $resteapayer), $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidPartially', $object->ref), 'confirm_paid_partially', $formquestion, "yes", 1, 420, 600);
5414 }
5415
5416 if ($action == 'deletepayment') {
5417 $payment_id = GETPOST('paiement_id');
5418 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&paiement_id='.$payment_id, $langs->trans('DeletePayment'), $langs->trans('ConfirmDeletePayment'), 'confirm_delete_paiement', '', 'no', 1);
5419 }
5420
5421 // Confirm deletion of product line
5422 if ($action == 'ask_deleteline') {
5423 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 'no', 1);
5424 }
5425
5426 // Confirm deletion of subtotal line
5427 if ($action == 'ask_subtotal_deleteline') {
5428 $langs->load("subtotals");
5429 $title = "DeleteSubtotalLine";
5430 $question = "ConfirmDeleteSubtotalLine";
5431 if (GETPOST('type') == 'title') {
5432 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
5433 $title = "DeleteTitleLine";
5434 $question = "ConfirmDeleteTitleLine";
5435 }
5436 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&lineid='.$lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
5437 }
5438
5439 // Clone confirmation
5440 if ($action == 'clone') {
5441 $filter = '(s.client:IN:1,2,3)';
5442 // Create an array for form
5443 $formquestion = array(
5444 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company($object->socid, 'socid', $filter, 1)),
5445 array('type' => 'date', 'name' => 'newdate', 'label' => $langs->trans("Date"), 'value' => dol_now())
5446 );
5447 // Request confirmation to clone
5448 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneInvoice', $object->ref), 'confirm_clone', $formquestion, 'yes', 1, 250);
5449 }
5450
5451 // Subtotal line form
5452 if ($action == 'add_title_line') {
5453 $langs->load('subtotals');
5454 $type = 'title';
5455 $depth_array = $object->getPossibleLevels($langs);
5456 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
5457 } elseif ($action == 'add_subtotal_line') {
5458 $langs->load('subtotals');
5459 $type = 'subtotal';
5460 $titles = $object->getPossibleTitles();
5461 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
5462 } elseif ($action == 'add_text_line') {
5463 $langs->load('subtotals');
5464 $type = 'text';
5465 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
5466 }
5467
5468 if ($action == "remove_file_comfirm") {
5469 $file = GETPOST('file', 'alpha');
5470
5471 $formconfirm = $form->formconfirm(
5472 $_SERVER["PHP_SELF"].'?facid='.$object->id.'&file='.urlencode($file),
5473 $langs->trans('DeleteFileHeader'),
5474 $langs->trans('DeleteFileText')."<br><br>".$file,
5475 'remove_file',
5476 '',
5477 'no',
5478 1
5479 );
5480 }
5481
5482 // Call Hook formConfirm
5483 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid, 'remainingtopay' => &$resteapayer);
5484 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
5485 if (empty($reshook)) {
5486 $formconfirm .= $hookmanager->resPrint;
5487 } elseif ($reshook > 0) {
5488 $formconfirm = $hookmanager->resPrint;
5489 }
5490
5491 // Print form confirm
5492 print $formconfirm;
5493
5494 // Invoice content
5495
5496 $linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
5497
5498 $morehtmlref = '<div class="refidno">';
5499 // Ref invoice
5500 if ($object->status == $object::STATUS_DRAFT && !$mysoc->isInEEC() && getDolGlobalString('INVOICE_ALLOW_FREE_REF')) {
5501 $morehtmlref .= $form->editfieldkey("Ref", 'ref', $object->ref, $object, (int) $usercancreate, 'string', '', 0, 1);
5502 $morehtmlref .= $form->editfieldval("Ref", 'ref', $object->ref, $object, (int) $usercancreate, 'string', '', null, null, '', 1);
5503 $morehtmlref .= '<br>';
5504 }
5505 // Ref customer
5506 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_customer, $object, (int) $usercancreate, 'string', '', 0, 1);
5507 $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_client', $object->ref_customer, $object, (int) $usercancreate, 'string'.(getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
5508 // Thirdparty
5509 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1, 'customer');
5510 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
5511 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$object->thirdparty->id.'">'.$langs->trans("OtherBills").'</a>)';
5512 }
5513 // Project
5514 if (isModEnabled('project')) {
5515 $langs->load("projects");
5516 $morehtmlref .= '<br>';
5517 if ($usercancreate) {
5518 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
5519 if ($action != 'classify') {
5520 $morehtmlref .= '<a class="editfielda" href="'.dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true).'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
5521 }
5522 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
5523 } else {
5524 if (!empty($object->fk_project)) {
5525 $proj = new Project($db);
5526 $proj->fetch($object->fk_project);
5527 $morehtmlref .= $proj->getNomUrl(1);
5528 if ($proj->title) {
5529 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
5530 }
5531 }
5532 }
5533 }
5534 $morehtmlref .= '</div>';
5535
5536 $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status
5537 $object->totalcreditnotes = $totalcreditnotes;
5538 $object->totaldeposits = $totaldeposits;
5539 $object->remaintopay = price2num($object->total_ttc - $object->totalpaid - $object->totalcreditnotes - $object->totaldeposits, 'MT');
5540
5541 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref, '', 0, '', '');
5542
5543 // Call Hook tabContentViewInvoice
5544 $parameters = array();
5545 // Note that $action and $object may be modified by hook
5546 $reshook = $hookmanager->executeHooks('tabContentViewInvoice', $parameters, $object, $action);
5547 if (empty($reshook)) {
5548 print '<div class="fichecenter">';
5549 print '<div class="fichehalfleft">';
5550 print '<div class="underbanner clearboth"></div>';
5551
5552 print '<table class="border centpercent tableforfield">';
5553
5554 // Type
5555 print '<tr><td class="fieldname_type">'.$langs->trans('Type').'</td><td class="valuefield fieldname_type">';
5556 print $object->getLibType(2);
5557 if ($object->subtype > 0) {
5558 print ' '.$object->getSubtypeLabel('facture');
5559 }
5560 if ($object->type == Facture::TYPE_REPLACEMENT) {
5561 $facreplaced = new Facture($db);
5562 $facreplaced->fetch($object->fk_facture_source);
5563 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("ReplaceInvoice", $facreplaced->getNomUrl(1, '', 32)).'</span>';
5564 }
5565 if ($object->type == Facture::TYPE_CREDIT_NOTE && !empty($object->fk_facture_source)) {
5566 $facusing = new Facture($db);
5567 $facusing->fetch($object->fk_facture_source);
5568 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("CorrectInvoice", $facusing->getNomUrl(1, '', 32)).'</span>';
5569 }
5570
5571 // Retrieve credit note ids (credit notes generated from this invoice)
5572 $object->getListIdAvoirFromInvoice();
5573
5574 if (!empty($object->creditnote_ids)) {
5575 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("InvoiceHasAvoir");
5576 $i = 0;
5577 foreach ($object->creditnote_ids as $invoiceid) {
5578 if ($i == 0) {
5579 print ' ';
5580 } else {
5581 print ',';
5582 }
5583 $creditnote = new Facture($db);
5584 $creditnote->fetch($invoiceid);
5585 print $creditnote->getNomUrl(1, '', 32);
5586 }
5587 print '</span>';
5588 }
5589 if ($objectidnext > 0) {
5590 $facthatreplace = new Facture($db);
5591 $facthatreplace->fetch($objectidnext);
5592 print ' <span class="opacitymediumbycolor paddingleft">'.str_replace('{s1}', $facthatreplace->getNomUrl(1), $langs->transnoentities("ReplacedByInvoice", '{s1}')).'</span>';
5593 }
5594
5596 $discount = new DiscountAbsolute($db);
5597 $result = $discount->fetch(0, $object->id);
5598 if ($result > 0) {
5599 print ' <span class="opacitymediumbycolor paddingleft">';
5600 $s = $langs->trans("CreditNoteConvertedIntoDiscount", '{s1}', '{s2}');
5601 $s = str_replace('{s1}', $object->getLibType(0), $s);
5602 $s = str_replace('{s2}', $discount->getNomUrl(1, 'discount'), $s);
5603 print $s;
5604 print '</span><br>';
5605 }
5606 }
5607
5608 if ($object->fk_fac_rec_source > 0) {
5609 $tmptemplate = new FactureRec($db);
5610 $result = $tmptemplate->fetch($object->fk_fac_rec_source);
5611 if ($result > 0) {
5612 print ' <span class="opacitymediumbycolor paddingleft">';
5613 $s = $langs->transnoentities("GeneratedFromTemplate", '{s1}');
5614 $s = str_replace('{s1}', $tmptemplate->getNomUrl(1, '', 32), $s);
5615 print $s;
5616 print '</span>';
5617 } else {
5618 print ' <span class="opacitymediumbycolor paddingleft">';
5619 print $langs->transnoentities("GeneratedFromTemplate", $langs->trans("ObjectDeleted"));
5620 print '</span>';
5621 }
5622 }
5623 print '</td></tr>';
5624
5625 // POS
5626 if (isModEnabled('takepos') || $object->module_source || getDolGlobalString('INVOICE_ALLOW_POS_SOURCE_EDIT')) {
5627 $langs->load("cashdesk");
5628 print '<tr><td class="fieldname_type">';
5629 print '<table class="nobordernopadding centpercent"><tr><td>';
5630 print $form->textwithpicto($langs->trans('PointOfSale'), $langs->trans('POSInfo'));
5631 print '</td>';
5632 if ($action != 'editposinfo' && $usercancreate) {
5633 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editposinfo&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetPOSInfo'), 1).'</a></td>';
5634 }
5635 print '</tr></table>';
5636 print '</td><td class="valuefield fieldname_type">';
5637 print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" name="formposinfo">';
5638 print '<input type="hidden" name="action" value="setposinfo">';
5639 print '<input type="hidden" name="token" value="' . newToken() . '">';
5640 if ($action == 'editposinfo') {
5641 print '<input type="text" class="maxwidth150" name="posmodule" placeholder="'.$langs->trans("POSModule").'" value="'.$object->module_source.'"> ';
5642 print '<input type="text" class="maxwidth100" name="posterminal" placeholder="'.$langs->trans("Terminal").'" value="'.$object->pos_source.'">';
5643 print '<input type="submit" class="button" name="submitposinfo" value="'.$langs->trans("Submit").'">';
5644 } else {
5645 if ($object->module_source) {
5646 print '<span class="opacitymediumbycolor paddingleft">'.dolPrintHTML(ucfirst($object->module_source).' - '.$langs->transnoentitiesnoconv("Terminal").' '.$object->pos_source).'</span>';
5647 }
5648 }
5649 print '</form>';
5650 print '</td></tr>';
5651 }
5652
5653 // Relative and absolute discounts
5654 print '<!-- Discounts -->'."\n";
5655 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td>';
5656 print '<td>';
5657 $thirdparty = $soc;
5658 $discount_type = 0;
5659 $backtopage = $_SERVER["PHP_SELF"].'?facid='.$object->id;
5660 $defaulttpldir = '/core/tpl';
5661 // loading object_discounts.tpl.php from module core/tpl if exists
5662 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5663 foreach ($dirtpls as $module => $reldir) {
5664 $res = 0;
5665 if (!empty($module)) {
5666 $tpl = dol_buildpath($reldir.'/object_discounts.tpl.php');
5667 } else {
5668 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/object_discounts.tpl.php';
5669 }
5670 if (file_exists($tpl)) {
5671 if (empty($conf->file->strict_mode)) {
5672 $res = @include $tpl;
5673 } else {
5674 $res = include $tpl;
5675 }
5676 }
5677 if ($res) {
5678 break;
5679 }
5680 }
5681
5682 print '</td></tr>';
5683
5684 // Date invoice
5685 print '<tr><td>';
5686 print '<table class="nobordernopadding centpercent"><tr><td>';
5687 print $langs->trans('DateInvoice');
5688 print '</td>';
5689 if ($action != 'editinvoicedate' && $object->status == $object::STATUS_DRAFT && $usercancreate && !getDolGlobalString('FAC_FORCE_DATE_VALIDATION')) {
5690 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editinvoicedate&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetDate'), 1).'</a></td>';
5691 }
5692 print '</tr></table>';
5693 print '</td><td>';
5694 if ($action == 'editinvoicedate') {
5695 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date, 'invoicedate');
5696 } else {
5697 print '<span class="valuedate">'.dol_print_date($object->date, 'day').'</span>';
5698 }
5699 print '</td>';
5700
5701 print '</tr>';
5702
5703 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
5704 // Date invoice point of tax (Leistungsdatum / service date for tax).
5705 // Only editable while the invoice is a draft -- once validated, the
5706 // invoice is a legally issued document and date_pointoftax is the
5707 // basis for the VAT-return period assignment under accrual taxation
5708 // (Soll-Versteuerung). To correct, set the invoice back to draft or
5709 // issue a credit note.
5710 $editable = ($usercancreate && $object->status < Facture::STATUS_VALIDATED);
5711 print '<tr><td>';
5712 print '<table class="nobordernopadding centpercent"><tr><td>';
5713 print $langs->trans('DatePointOfTax');
5714 print '</td>';
5715 if ($editable) {
5716 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editdate_pointoftax&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetDate'), 1).'</a></td>';
5717 }
5718 print '</tr></table>';
5719 print '</td><td>';
5720 if ($action == 'editdate_pointoftax' && $editable) {
5721 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date_pointoftax, 'date_pointoftax');
5722 } else {
5723 print '<span class="valuedate">'.dol_print_date($object->date_pointoftax, 'day').'</span>';
5724 }
5725 print '</td></tr>';
5726 }
5727
5728 // Source reason (why we have an invoice)
5729 if (!getDolGlobalInt('INVOICE_DISABLE_SOURCE')) {
5730 print '<tr><td>';
5731 print $form->editfieldkey('Source', 'input_reason', '', $object, (int) $usercancreate);
5732 print '</td><td class="valuefield">';
5733 if ($action == 'editinput_reason') {
5734 $form->formInputReason($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->demand_reason_id, 'input_reason_id', 1, 'maxwidth250 widthcentpercentminusx');
5735 } else {
5736 $form->formInputReason($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->demand_reason_id, 'none');
5737 }
5738 print '</td></tr>';
5739 }
5740
5741 // Payment term
5742 print '<tr><td>';
5743 print '<table class="nobordernopadding centpercent"><tr><td>';
5744 print $langs->trans('PaymentConditionsShort');
5745 print '</td>';
5746 if ($object->type != Facture::TYPE_CREDIT_NOTE && $action != 'editconditions' && $usercancreate) {
5747 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editconditions&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetConditions'), 1).'</a></td>';
5748 }
5749 print '</tr></table>';
5750 print '</td><td>';
5751 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
5752 if ($action == 'editconditions') {
5753 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->cond_reglement_id, 'cond_reglement_id');
5754 } else {
5755 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->cond_reglement_id, 'none');
5756 }
5757 }
5758 print '</td></tr>';
5759
5760 // Date payment term
5761 print '<tr><td>';
5762 print '<table class="nobordernopadding centpercent"><tr><td>';
5763 print $langs->trans('DateMaxPayment');
5764 print '</td>';
5765 if ($object->type != Facture::TYPE_CREDIT_NOTE && $action != 'editpaymentterm' && $usercancreate) {
5766 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editpaymentterm&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetDate'), 1).'</a></td>';
5767 }
5768 print '</tr></table>';
5769 print '</td><td>';
5770 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
5771 if ($action == 'editpaymentterm') {
5772 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date_lim_reglement, 'paymentterm');
5773 } else {
5774 print '<span class="valuedate">'.dol_print_date($object->date_lim_reglement, 'day').'</span>';
5775 if ($object->hasDelay()) {
5776 print img_warning($langs->trans('Late'));
5777 }
5778 }
5779 }
5780 print '</td></tr>';
5781
5782 // Payment mode
5783 print '<tr><td>';
5784 print '<table class="nobordernopadding centpercent"><tr><td>';
5785 print $langs->trans('PaymentMode');
5786 print '</td>';
5787 if ($action != 'editmode' && $usercancreate) {
5788 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editmode&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetMode'), 1).'</a></td>';
5789 }
5790 print '</tr></table>';
5791 print '</td><td>';
5792 if ($action == 'editmode') {
5793 $form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
5794 } else {
5795 $form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->mode_reglement_id, 'none', 'CRDT');
5796 }
5797 print '</td></tr>';
5798
5799 // Bank Account
5800 if (isModEnabled("bank")) {
5801 print '<tr><td class="nowrap">';
5802 print '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
5803 print $langs->trans('DefaultBankAccount');
5804 print '<td>';
5805 if (($action != 'editbankaccount') && $usercancreate) {
5806 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editbankaccount&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->trans('SetBankAccount'), 1).'</a></td>';
5807 }
5808 print '</tr></table>';
5809 print '</td><td>';
5810 if ($action == 'editbankaccount') {
5811 $form->formSelectAccount($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->fk_account, 'fk_account', 1);
5812 } else {
5813 $form->formSelectAccount($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->fk_account, 'none');
5814 }
5815 print "</td>";
5816 print '</tr>';
5817 }
5818
5819 // Incoterms
5820 if (isModEnabled('incoterm')) {
5821 print '<tr><td>';
5822 print '<table class="nobordernopadding centpercent"><tr><td>';
5823 print $langs->trans('IncotermLabel');
5824 print '<td><td class="right">';
5825 if ($usercancreate) {
5826 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=editincoterm&token='.newToken().'">'.img_edit().'</a>';
5827 }
5828 print '</td></tr></table>';
5829 print '</td>';
5830 print '<td>';
5831 if ($action != 'editincoterm') {
5832 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
5833 } else {
5834 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'].'?id='.$object->id);
5835 }
5836 print '</td></tr>';
5837 }
5838
5839 // Dispute open
5840 print '<tr><td>';
5841 print '<table class="nobordernopadding centpercent"><tr><td>';
5842 print $langs->trans('Dispute');
5843 print '<td><td class="right">';
5844 if ($usercancreate) {
5845 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=editdispute_status&token='.newToken().'">'.img_edit().'</a>';
5846 }
5847 print '</td></tr></table>';
5848 print '</td><td>';
5849 $liststatus = Facture::ARRAY_OF_DISPUTE_STATUS;
5850 if ($action != 'editdispute_status') {
5851 if ($object->dispute_status) {
5852 print $langs->trans($liststatus[$object->dispute_status]['label']);
5853 }
5854 } else {
5855 print '<form enctype="multipart/form-data" action="'.DOL_URL_ROOT.'/compta/facture/card.php" method="POST">';
5856 print '<input type="hidden" name="action" value="set_dispute_status">';
5857 print '<input type="hidden" name="token" value="'.newToken().'">';
5858 print '<input type="hidden" name="id" value="'.$object->id.'">';
5859 print '<input type="hidden" name="page_y" value="">';
5860 print $form->selectarray('dispute_status', $liststatus, $object->dispute_status, 0, 0, 0, '', 1);
5861 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Save").'">';
5862 print '</form>';
5863 }
5864 print '</td></tr>';
5865
5866 // Categories
5867 if (isModEnabled('category')) {
5868 print '<tr><td>';
5869 print '<table class="nobordernopadding centpercent"><tr><td>';
5870 print $langs->trans("Categories");
5871 print '<td><td class="right">';
5872 if ($usercancreate) {
5873 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
5874 } else {
5875 print '&nbsp;';
5876 }
5877 print '</td></tr></table>';
5878 print '</td>';
5879 print '<td>';
5880 if ($action == 'edittags') {
5881 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5882 print '<input type="hidden" name="action" value="settags">';
5883 print '<input type="hidden" name="token" value="'.newToken().'">';
5884 print $form->selectCategories(Categorie::TYPE_INVOICE, 'categories', $object);
5885 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5886 print '</form>';
5887 } else {
5888 print $form->showCategories($object->id, Categorie::TYPE_INVOICE, 1);
5889 }
5890 print "</td></tr>";
5891 }
5892
5893 $displayWarranty = false;
5894
5895 if (!empty($object->retained_warranty) || getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
5896 $displayWarranty = true;
5897 if (!in_array($object->type, $retainedWarrantyInvoiceAvailableType) && empty($object->retained_warranty)) {
5898 $displayWarranty = false;
5899 }
5900
5901 if ($displayWarranty) {
5902 // Retained Warranty
5903 print '<tr class="retained-warranty-lines" ><td>';
5904 print '<table id="retained-warranty-table" class="nobordernopadding centpercent"><tr><td>';
5905 print $langs->trans('RetainedWarranty');
5906 print '</td>';
5907 if ($action != 'editretainedwarranty' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5908 print '<td align="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editretainedwarranty&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('setRetainedWarranty'), 1).'</a></td>';
5909 }
5910
5911 print '</tr></table>';
5912 print '</td><td>';
5913 if ($action == 'editretainedwarranty' && $object->status == Facture::STATUS_DRAFT) {
5914 print '<form id="retained-warranty-form" method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5915 print '<input type="hidden" name="action" value="setretainedwarranty">';
5916 print '<input type="hidden" name="token" value="'.newToken().'">';
5917 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5918 print '<input name="retained_warranty" type="number" step="0.01" min="0" max="100" value="'.$object->retained_warranty.'" >';
5919 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5920 print '</form>';
5921 } else {
5922 print price($object->retained_warranty).'%';
5923 }
5924 print '</td></tr>';
5925
5926 // Retained warranty payment term
5927 print '<tr class="retained-warranty-lines" ><td>';
5928 print '<table id="retained-warranty-cond-reglement-table" class="nobordernopadding" width="100%"><tr><td>';
5929 print $langs->trans('PaymentConditionsShortRetainedWarranty');
5930 print '</td>';
5931 if ($action != 'editretainedwarrantypaymentterms' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5932 print '<td align="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editretainedwarrantypaymentterms&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('setPaymentConditionsShortRetainedWarranty'), 1).'</a></td>';
5933 }
5934
5935 print '</tr></table>';
5936 print '</td><td>';
5937 $defaultDate = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : strtotime('-1 years', $object->date_lim_reglement);
5938 if ($object->date > $defaultDate) {
5939 $defaultDate = $object->date;
5940 }
5941
5942 if ($action == 'editretainedwarrantypaymentterms' && $object->status == Facture::STATUS_DRAFT) {
5943 //date('Y-m-d',$object->date_lim_reglement)
5944 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5945 print '<input type="hidden" name="action" value="setretainedwarrantyconditions">';
5946 print '<input type="hidden" name="token" value="'.newToken().'">';
5947 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5948 $retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
5949 $retained_warranty_fk_cond_reglement = !empty($retained_warranty_fk_cond_reglement) ? $retained_warranty_fk_cond_reglement : $object->retained_warranty_fk_cond_reglement;
5950 $retained_warranty_fk_cond_reglement = !empty($retained_warranty_fk_cond_reglement) ? $retained_warranty_fk_cond_reglement : getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
5951 print $form->getSelectConditionsPaiements($retained_warranty_fk_cond_reglement, 'retained_warranty_fk_cond_reglement', -1, 1);
5952 print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
5953 print '</form>';
5954 } else {
5955 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->retained_warranty_fk_cond_reglement, 'none');
5956 if (!$displayWarranty) {
5957 print img_picto($langs->trans('RetainedWarrantyNeed100Percent'), 'warning', 'class="pictowarning valignmiddle" ');
5958 }
5959 }
5960 print '</td></tr>';
5961
5962 // Retained Warranty payment date limit
5963 print '<tr class="retained-warranty-lines" ><td>';
5964 print '<table id="retained-warranty-date-limit-table" class="nobordernopadding" width="100%"><tr><td>';
5965 print $langs->trans('RetainedWarrantyDateLimit');
5966 print '</td>';
5967 if ($action != 'editretainedwarrantydatelimit' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5968 print '<td align="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editretainedwarrantydatelimit&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('setRetainedWarrantyDateLimit'), 1).'</a></td>';
5969 }
5970
5971 print '</tr></table>';
5972 print '</td><td>';
5973 $defaultDate = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : strtotime('-1 years', $object->date_lim_reglement);
5974 if ($object->date > $defaultDate) {
5975 $defaultDate = $object->date;
5976 }
5977
5978 if ($action == 'editretainedwarrantydatelimit' && $object->status == Facture::STATUS_DRAFT) {
5979 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5980 print '<input type="hidden" name="action" value="setretainedwarrantydatelimit">';
5981 print '<input type="hidden" name="token" value="'.newToken().'">';
5982 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5983 //print '<input name="retained_warranty_date_limit" type="date" step="1" min="'.dol_print_date($object->date, '%Y-%m-%d').'" value="'.dol_print_date($defaultDate, '%Y-%m-%d').'" >'; Time Input not standard
5984 print $form->selectDate($defaultDate, 'retained_warranty_date_limit');
5985 print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
5986 print '</form>';
5987 } else {
5988 print dol_print_date($object->retained_warranty_date_limit, 'day');
5989 }
5990 print '</td></tr>';
5991 }
5992 }
5993
5994
5995 // Other attributes
5996 $cols = 2;
5997 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
5998
5999 print '</table>';
6000
6001 print '</div>';
6002 print '<div class="fichehalfright">';
6003
6004 print '<!-- amounts -->'."\n";
6005 print '<div class="underbanner clearboth"></div>'."\n";
6006
6007 print '<table class="border tableforfield centpercent">';
6008
6009 include DOL_DOCUMENT_ROOT.'/core/tpl/object_currency_amount.tpl.php';
6010
6011 $sign = 1;
6012 if (getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE_SCREEN') && $object->type == $object::TYPE_CREDIT_NOTE) {
6013 $sign = -1; // We invert sign for output
6014 }
6015 print '<tr>';
6016 // Amount HT
6017 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
6018 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_ht, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
6019 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
6020 // Multicurrency Amount HT
6021 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_ht, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
6022 }
6023 print '</tr>';
6024
6025 print '<tr>';
6026 // Amount VAT
6027 print '<td>' . $langs->trans('AmountVAT') . '</td>';
6028 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_tva, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
6029 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
6030 // Multicurrency Amount VAT
6031 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_tva, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
6032 }
6033 print '</tr>';
6034
6035 // Amount Local Taxes
6036 if (($mysoc->localtax1_assuj == "1" && $mysoc->useLocalTax(1)) || $object->total_localtax1 != 0) {
6037 print '<tr>';
6038 print '<td class="titlefieldmiddle">' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
6039 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
6040 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
6041 $object->multicurrency_total_localtax1 = (float) price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
6042
6043 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_localtax1, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
6044 }
6045 print '</tr>';
6046 }
6047
6048 if (($mysoc->localtax2_assuj == "1" && $mysoc->useLocalTax(2)) || $object->total_localtax2 != 0) {
6049 print '<tr>';
6050 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
6051 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
6052 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
6053 $object->multicurrency_total_localtax2 = (float) price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
6054
6055 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_localtax2, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
6056 }
6057 print '</tr>';
6058 }
6059
6060 // Add the revenue stamp
6061 if ($selleruserevenustamp) {
6062 print '<tr><td class="titlefieldmiddle">';
6063 print '<table class="nobordernopadding centpercent"><tr><td>';
6064 print $langs->trans('RevenueStamp');
6065 print '</td>';
6066 if ($action != 'editrevenuestamp' && $object->status == $object::STATUS_DRAFT && $usercancreate) {
6067 print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editrevenuestamp&token='.newToken().'&facid='.$object->id.'">'.img_edit($langs->trans('SetRevenuStamp'), 1).'</a></td>';
6068 }
6069 print '</tr></table>';
6070 print '</td><td class="nowrap amountcard right">';
6071 if ($action == 'editrevenuestamp') {
6072 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
6073 print '<input type="hidden" name="token" value="'.newToken().'">';
6074 print '<input type="hidden" name="action" value="setrevenuestamp">';
6075 print '<input type="hidden" name="revenuestamp" id="revenuestamp_val" value="'.price2num($object->revenuestamp).'">';
6076 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
6077 print $formother->select_revenue_stamp('', 'revenuestamp_type', $mysoc->country_code);
6078 print ' &rarr; <span id="revenuestamp_span"></span>';
6079 print ' <input type="submit" class="button buttongen button-save small" value="'.$langs->trans('Modify').'">';
6080 print '</form>';
6081 print " <script>
6082 $(document).ready(function(){
6083 js_recalculate_revenuestamp();
6084 $('select[name=revenuestamp_type]').on('change',function(){
6085 js_recalculate_revenuestamp();
6086 });
6087 });
6088 function js_recalculate_revenuestamp(){
6089 var valselected = $('select[name=revenuestamp_type]').val();
6090 console.log('Calculate revenue stamp from '+valselected);
6091 var revenue = 0;
6092 if (valselected.indexOf('%') == -1)
6093 {
6094 revenue = valselected;
6095 }
6096 else
6097 {
6098 var revenue_type = parseFloat(valselected);
6099 var amount_net = ".round($object->total_ht, 2).";
6100 revenue = revenue_type * amount_net / 100;
6101 revenue = revenue.toFixed(2);
6102 }
6103 $('#revenuestamp_val').val(revenue);
6104 $('#revenuestamp_span').html(revenue);
6105 }
6106 </script>";
6107 } else {
6108 print price($object->revenuestamp, 1, '', 1, -1, -1, $conf->currency);
6109 }
6110 print '</td></tr>';
6111 }
6112
6113 print '<tr>';
6114 // Amount TTC
6115 print '<td>' . $langs->trans('AmountTTC') . '</td>';
6116 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_ttc, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
6117 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
6118 // Multicurrency Amount TTC
6119 print '<td class="nowrap amountcard right">' . price($sign * $object->multicurrency_total_ttc, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
6120 }
6121 print '</tr>';
6122
6123 print '</table>';
6124
6125 $nbrows = 8;
6126 $nbcols = 3;
6127 if (isModEnabled('project')) {
6128 $nbrows++;
6129 }
6130 if (isModEnabled("bank")) {
6131 $nbrows++;
6132 $nbcols++;
6133 }
6134 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
6135 $nbrows++;
6136 }
6137 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
6138 $nbrows++;
6139 }
6140 if ($selleruserevenustamp) {
6141 $nbrows++;
6142 }
6143 if (isModEnabled('multicurrency')) {
6144 $nbrows += 5;
6145 }
6146 if (isModEnabled('incoterm')) {
6147 $nbrows += 1;
6148 }
6149
6150 $total_prev_ht = $total_prev_ttc = 0;
6151 $total_global_ht = $total_global_ttc = 0;
6152
6153 // List of previous situation invoices
6154 if (($object->situation_cycle_ref > 0) && getDolGlobalString('INVOICE_USE_SITUATION')) {
6155 print '<!-- List of situation invoices -->';
6156 print '<div class="div-table-responsive-no-min">';
6157 print '<table class="noborder paymenttable centpercent situationstable">';
6158
6159 print '<tr class="liste_titre">';
6160 print '<td>'.$langs->trans('ListOfSituationInvoices').'</td>';
6161 print '<td></td>';
6162 print '<td class="center">'.$langs->trans('Situation').'</td>';
6163
6164 if (isModEnabled("bank")) {
6165 print '<td class="right"></td>';
6166 }
6167 print '<td class="right">'.$langs->trans('AmountHT').'</td>';
6168 print '<td class="right">'.$langs->trans('AmountTTC').'</td>';
6169 print '<td width="18">&nbsp;</td>';
6170 print '</tr>';
6171
6172 if (count($object->tab_previous_situation_invoice) > 0) {
6173 // List of previous invoices
6174
6175 $current_situation_counter = array();
6176 foreach ($object->tab_previous_situation_invoice as $prev_invoice) {
6177 $tmptotalallpayments = $prev_invoice->getSommePaiement(0);
6178 $tmptotalallpayments += $prev_invoice->getSumDepositsUsed(0);
6179 $tmptotalallpayments += $prev_invoice->getSumCreditNotesUsed(0);
6180
6181 $total_prev_ht += $prev_invoice->total_ht;
6182 $total_prev_ttc += $prev_invoice->total_ttc;
6183
6184 $current_situation_counter[] = (($prev_invoice->type == Facture::TYPE_CREDIT_NOTE) ? -1 : 1) * $prev_invoice->situation_counter;
6185 print '<tr class="oddeven">';
6186 print '<td>'.$prev_invoice->getNomUrl(1).'</td>';
6187 print '<td></td>';
6188 print '<td align="center" >'.(($prev_invoice->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$prev_invoice->situation_counter.'</td>';
6189 if (isModEnabled("bank")) {
6190 print '<td class="right"></td>';
6191 }
6192 print '<td class="right"><span class="amount">'.price($prev_invoice->total_ht).'</span></td>';
6193 print '<td class="right"><span class="amount">'.price($prev_invoice->total_ttc).'</span></td>';
6194 print '<td class="right">'.$prev_invoice->getLibStatut(3, $tmptotalallpayments).'</td>';
6195 print '</tr>';
6196 }
6197 }
6198
6199 $totalallpayments = $object->getSommePaiement(0);
6200 $totalallpayments += $object->getSumCreditNotesUsed(0);
6201 $totalallpayments += $object->getSumDepositsUsed(0);
6202
6203 $total_global_ht += $total_prev_ht;
6204 $total_global_ttc += $total_prev_ttc;
6205 $total_global_ht += $object->total_ht;
6206 $total_global_ttc += $object->total_ttc;
6207
6208 $current_situation_counter[] = (($object->type == Facture::TYPE_CREDIT_NOTE) ? -1 : 1) * $object->situation_counter;
6209
6210 print '<tr class="oddeven">';
6211 print '<td>'.$object->getNomUrl(1).'</td>';
6212 print '<td></td>';
6213 print '<td class="center">'.(($object->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$object->situation_counter.'</td>';
6214 if (isModEnabled("bank")) {
6215 print '<td class="right"></td>';
6216 }
6217 print '<td class="right"><span class="amount">'.price($object->total_ht).'</span></td>';
6218 print '<td class="right"><span class="amount">'.price($object->total_ttc).'</span></td>';
6219 print '<td class="right">'.$object->getLibStatut(3, $totalallpayments).'</td>';
6220 print '</tr>';
6221
6222
6223 print '<tr class="oddeven">';
6224 print '<td colspan="2" class="left"><b>'.$langs->trans('SituationTotalAfterInvoice').'</b></td>';
6225 print '<td>';
6226 $i = 0;
6227 foreach ($current_situation_counter as $sit) {
6228 $curSign = $sit > 0 ? '+' : '-';
6229 $curType = $sit > 0 ? $langs->trans('situationInvoiceShortcode_S') : $langs->trans('situationInvoiceShortcode_AS');
6230 if ($i > 0) {
6231 print ' '.$curSign.' ';
6232 }
6233 print $curType.abs($sit);
6234 $i++;
6235 }
6236 print '</td>';
6237 if (isModEnabled("bank")) {
6238 print '<td></td>';
6239 }
6240 print '<td class="right"><b>'.price($total_global_ht).'</b></td>';
6241 print '<td class="right"><b>'.price($total_global_ttc).'</b></td>';
6242 print '<td width="18">&nbsp;</td>';
6243 print '</tr>';
6244
6245
6246 if (count($object->tab_next_situation_invoice) > 0) {
6247 // List of next invoices
6248 $total_next_ht = $total_next_ttc = 0;
6249
6250 foreach ($object->tab_next_situation_invoice as $next_invoice) {
6251 $next_totalpaid = $next_invoice->getSommePaiement(0);
6252 $next_totalcreditnotes = $next_invoice->getSumCreditNotesUsed(0);
6253 $next_totaldeposits = $next_invoice->getSumDepositsUsed(0);
6254 $total_next_ht += $next_invoice->total_ht;
6255 $total_next_ttc += $next_invoice->total_ttc;
6256
6257 print '<tr class="oddeven">';
6258 print '<td>'.$next_invoice->getNomUrl(1).'</td>';
6259 print '<td></td>';
6260 print '<td class="center">'.(($next_invoice->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$next_invoice->situation_counter.'</td>';
6261 if (isModEnabled("bank")) {
6262 print '<td class="right"></td>';
6263 }
6264 print '<td class="right"><span class="amount">'.price($next_invoice->total_ht).'</span></td>';
6265 print '<td class="right"><span class="amount">'.price($next_invoice->total_ttc).'</span></td>';
6266 print '<td class="right">'.$next_invoice->getLibStatut(3, $next_totalpaid + $next_totalcreditnotes + $next_totaldeposits).'</td>';
6267 print '</tr>';
6268 }
6269
6270 $total_global_ht += $total_next_ht;
6271 $total_global_ttc += $total_next_ttc;
6272
6273 print '<tr class="oddeven">';
6274 print '<td colspan="3" class="right"></td>';
6275 if (isModEnabled("bank")) {
6276 print '<td class="right"></td>';
6277 }
6278 print '<td class="right"><b>'.price($total_global_ht).'</b></td>';
6279 print '<td class="right"><b>'.price($total_global_ttc).'</b></td>';
6280 print '<td width="18">&nbsp;</td>';
6281 print '</tr>';
6282 }
6283
6284 print '</table>';
6285 print '</div>';
6286 }
6287
6288 $sign = 1;
6289 if ($object->type == $object::TYPE_CREDIT_NOTE) {
6290 $sign = -1;
6291 }
6292
6293 // List of payments already done
6294
6295 print '<!-- List of payments already done -->';
6296 print '<div class="div-table-responsive-no-min">';
6297 print '<table class="noborder paymenttable centpercent">';
6298
6299 print '<tr class="liste_titre">';
6300 print '<td class="liste_titre">'.($object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("PaymentsBack") : $langs->trans('Payments')).'</td>';
6301 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('Date').'</span></td>';
6302 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('Type').'</span></td>';
6303 if (isModEnabled("bank")) {
6304 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('BankAccount').'</span></td>';
6305 }
6306 print '<td class="liste_titre"></td>';
6307 print '<td class="liste_titre right">'.$langs->trans('Amount').'</td>';
6308 print '</tr>';
6309
6310 // Payments already done (from payment on this invoice)
6311 $sql = 'SELECT p.datep as dp, p.ref, p.num_paiement as num_payment, p.rowid, p.fk_bank,';
6312 $sql .= ' c.code as payment_code, c.libelle as payment_label,';
6313 $sql .= ' pf.amount,';
6314 $sql .= ' ba.rowid as baid, ba.ref as baref, ba.label, ba.number as banumber, ba.account_number, ba.fk_accountancy_journal, ba.currency_code as bacurrency_code';
6315 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
6316 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
6317 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON p.fk_bank = b.rowid';
6318 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank_account as ba ON b.fk_account = ba.rowid';
6319 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id).' AND pf.fk_paiement = p.rowid';
6320 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
6321 $sql .= ' ORDER BY p.datep, p.tms';
6322
6323 $result = $db->query($sql);
6324 if ($result) {
6325 $num = $db->num_rows($result);
6326 $i = 0;
6327
6328 if ($num > 0) {
6329 while ($i < $num) {
6330 $objp = $db->fetch_object($result);
6331
6332 $paymentstatic->id = $objp->rowid;
6333 $paymentstatic->datepaye = $db->jdate($objp->dp);
6334 $paymentstatic->ref = $objp->ref;
6335 $paymentstatic->num_payment = $objp->num_payment;
6336 $paymentstatic->paiementcode = $objp->payment_code;
6337
6338 print '<tr class="oddeven">';
6339 print '<td class="nowraponall">';
6340 print $paymentstatic->getNomUrl(1);
6341 print '</td>';
6342 print '<td>';
6343 $dateofpayment = $db->jdate($objp->dp);
6344 $tmparray = dol_getdate($dateofpayment);
6345 if ($tmparray['seconds'] == 0 && $tmparray['minutes'] == 0 && ($tmparray['hours'] == 0 || $tmparray['hours'] == 12)) { // We set hours to 0:00 or 12:00 because we don't know it
6346 print dol_print_date($dateofpayment, 'day');
6347 } else { // Hours was set to real date of payment (special case for POS for example)
6348 print dol_print_date($dateofpayment, 'dayhour', 'tzuser');
6349 }
6350 print '</td>';
6351
6352 $label = ($langs->trans("PaymentType".$objp->payment_code) != "PaymentType".$objp->payment_code) ? $langs->trans("PaymentType".$objp->payment_code) : $objp->payment_label;
6353 print '<td class="tdoverflowmax80" title="'.dol_escape_htmltag($label.' '.$objp->num_payment).'">'.dol_escape_htmltag($label.' '.$objp->num_payment).'</td>';
6354 if (isModEnabled("bank")) {
6355 $bankaccountstatic->id = $objp->baid;
6356 $bankaccountstatic->ref = $objp->baref;
6357 $bankaccountstatic->label = $objp->baref;
6358 $bankaccountstatic->number = $objp->banumber;
6359 $bankaccountstatic->currency_code = $objp->bacurrency_code;
6360
6361 if (isModEnabled('accounting')) {
6362 $bankaccountstatic->account_number = $objp->account_number;
6363
6364 $accountingjournal = new AccountingJournal($db);
6365 $accountingjournal->fetch($objp->fk_accountancy_journal);
6366 $bankaccountstatic->accountancy_journal = $accountingjournal->getNomUrl(0, 1, 1, '', 1);
6367 }
6368
6369 print '<td class="nowraponall">';
6370 if ($bankaccountstatic->id) {
6371 print $bankaccountstatic->getNomUrl(1, 'transactions');
6372 }
6373 print '</td>';
6374 }
6375
6376 // Delete
6377 print '<td class="center">';
6378 $paiement = new Paiement($db);
6379 $paiement->fetch($objp->rowid);
6380 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $user->socid == 0 && !$paiement->isReconciled()) {
6381 print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deletepayment&token='.newToken().'&paiement_id='.$objp->rowid.'">';
6382 print img_delete();
6383 print '</a>';
6384 }
6385 print '</td>';
6386
6387 // Amount
6388 print '<td class="right"><span class="amount">'.price($sign * $objp->amount).'</span></td>';
6389
6390 print '</tr>';
6391 $i++;
6392 }
6393 }
6394
6395 $db->free($result);
6396 } else {
6398 }
6399
6400 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
6401 // Total already paid
6402 print '<tr>';
6403 print '<td colspan="'.($nbcols + 1).'" class="right">';
6404 print '<span class="opacitymedium">';
6405 if ($object->type != Facture::TYPE_DEPOSIT) {
6406 print $langs->trans('AlreadyPaidNoCreditNotesNoDeposits');
6407 } else {
6408 print $langs->trans('AlreadyPaid');
6409 }
6410 print '</span></td>';
6411 //print '<td></td>';
6412 print '<td class="right'.(($totalpaid > 0) ? ' amountalreadypaid' : '').'">'.price($totalpaid).'</td>';
6413 print '</tr>';
6414
6415 $resteapayeraffiche = $resteapayer;
6416 $cssforamountpaymentcomplete = 'amountpaymentcomplete';
6417 $cssforamountpaymentcompletenoresize = 'amountpaymentcompletenoresize';
6418
6419 // Loop on each credit note or deposit amount applied
6420 $creditnoteamount = 0;
6421 $depositamount = 0;
6422 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
6423 $sql .= " re.description, re.fk_facture_source";
6424 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
6425 $sql .= " WHERE fk_facture = ".((int) $object->id);
6426 $resql = $db->query($sql);
6427 if ($resql) {
6428 $num = $db->num_rows($resql);
6429 $i = 0;
6430 $invoice = new Facture($db);
6431 while ($i < $num) {
6432 $obj = $db->fetch_object($resql);
6433 $invoice->fetch($obj->fk_facture_source);
6434 print '<tr>';
6435 print '<td colspan="'.$nbcols.'" class="right">';
6436 print '<span class="opacitymedium">';
6437 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
6438 print $langs->trans("CreditNote").' ';
6439 }
6440 if ($invoice->type == Facture::TYPE_DEPOSIT) {
6441 print $langs->trans("Deposit").' ';
6442 }
6443 print $invoice->getNomUrl(0);
6444 print '</span>';
6445 print '</td>';
6446 // Delete discount
6447 print '<td class="right">';
6448 print '<a href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=unlinkdiscount&token='.newToken().'&discountid='.$obj->rowid.'">';
6449 print img_picto($langs->transnoentitiesnoconv("RemoveDiscount"), 'unlink');
6450 print '</a>';
6451 print '</td>';
6452 // Amount
6453 print '<td class="right"><span class="amount">'.price($obj->amount_ttc).'</span></td>';
6454 print '</tr>';
6455 $i++;
6456 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
6457 $creditnoteamount += $obj->amount_ttc;
6458 }
6459 if ($invoice->type == Facture::TYPE_DEPOSIT) {
6460 $depositamount += $obj->amount_ttc;
6461 }
6462 }
6463 } else {
6465 }
6466
6467 // Partially paid 'discount'
6468 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'discount_vat') {
6469 print '<tr>';
6470 print '<td colspan="'.$nbcols.'" class="nowrap right">';
6471 print '<span class="opacitymedium">';
6472 print $form->textwithpicto($langs->trans("Discount"), $langs->trans("HelpEscompte"), - 1);
6473 print '</span>';
6474 print '</td>';
6475 print '<td></td>';
6476 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6477 print '</tr>';
6478 $resteapayeraffiche = 0;
6479 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6480 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6481 }
6482 // Partially paid or abandoned 'badcustomer'
6483 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'badcustomer') {
6484 print '<tr>';
6485 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6486 print '<span class="opacitymedium">';
6487 print $form->textwithpicto($langs->trans("Abandoned"), $langs->trans("HelpAbandonBadCustomer"), - 1);
6488 print '</span>';
6489 print '</td>';
6490 //print '<td></td>';
6491 print '<td class="right">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</td>';
6492 print '</tr>';
6493 // $resteapayeraffiche=0;
6494 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6495 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6496 }
6497 // Partially paid or abandoned 'product_returned'
6498 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'product_returned') {
6499 print '<tr>';
6500 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6501 print '<span class="opacitymedium">';
6502 print $form->textwithpicto($langs->trans("ProductReturned"), $langs->trans("HelpAbandonProductReturned"), - 1);
6503 print '</span>';
6504 print '</td>';
6505 //print '<td></td>';
6506 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6507 print '</tr>';
6508 $resteapayeraffiche = 0;
6509 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6510 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6511 }
6512 // Partially paid or abandoned 'abandoned'
6513 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'abandon') {
6514 print '<tr>';
6515 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6516 $text = $langs->trans("HelpAbandonOther");
6517 if ($object->close_note) {
6518 $text .= '<br><br><b>'.$langs->trans("Reason").'</b>:'.$object->close_note;
6519 }
6520 print '<span class="opacitymedium">';
6521 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6522 print $form->textwithpicto($langs->trans("Abandoned"), $text, - 1);
6523 print '</span>';
6524 print '</td>';
6525 //print '<td></td>';
6526 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6527 print '</tr>';
6528 $resteapayeraffiche = 0;
6529 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6530 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6531 }
6532
6533 // Billed
6534 print '<tr>';
6535 print '<td colspan="'.($nbcols + 1).'" class="right">';
6536 print '<span class="opacitymedium">';
6537 print $langs->trans("Billed");
6538 print '</td>';
6539 //print '<td></td>';
6540 print '<td class="right">'.price($object->total_ttc).'</td>';
6541 print '</tr>';
6542
6543 // Remainder to pay
6544 print '<tr>';
6545 print '<td colspan="'.($nbcols + 1).'" class="right">';
6546 print '<span class="opacitymedium">';
6547 print $langs->trans('RemainderToPay');
6548 if ($resteapayeraffiche < 0) {
6549 print ' ('.$langs->trans('NegativeIfExcessReceived').')';
6550 }
6551 print '</span>';
6552 print '</td>';
6553 //print '<td></td>';
6554 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopay' : (' '.$cssforamountpaymentcomplete)).'">'.price($resteapayeraffiche).'</td>';
6555 print '</tr>';
6556
6557 // Remainder to pay Multicurrency
6558 if (isModEnabled('multicurrency') && $object->multicurrency_code && ($object->multicurrency_code != $conf->currency || $object->multicurrency_tx != 1)) {
6559 print '<tr>';
6560 print '<td colspan="'.($nbcols + 1).'" class="right">';
6561 print '<span class="opacitymedium">';
6562 print $langs->trans('RemainderToPayMulticurrency');
6563 if ($resteapayeraffiche < 0) {
6564 print ' ('.$langs->trans('NegativeIfExcessReceived').')';
6565 }
6566 print '</span>';
6567 print '</td>';
6568 //print '<td></td>';
6569 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopaynoresize' : (' '.$cssforamountpaymentcompletenoresize)).'">';
6570 //print (empty($object->multicurrency_code) ? $conf->currency : $object->multicurrency_code).' ';
6571 print price(price2num($object->multicurrency_tx * $resteapayeraffiche, 'MT'), 1, $langs, 1, -1, -1, (empty($object->multicurrency_code) ? $conf->currency : $object->multicurrency_code)).'</td>';
6572 print '</tr>';
6573 }
6574
6575 // Retained warranty : usually use on construction industry
6576 if (!empty($object->situation_final) && !empty($object->retained_warranty) && $displayWarranty) {
6577 // Billed - retained warranty
6578 if ($object->isSituationInvoice()) {
6579 $retainedWarranty = $total_global_ttc * $object->retained_warranty / 100;
6580 } else {
6581 // Because one day retained warranty could be used on standard invoices
6582 $retainedWarranty = $object->total_ttc * $object->retained_warranty / 100;
6583 }
6584
6585 $billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
6586
6587 print '<tr>';
6588 print '<td colspan="'.($nbcols + 1).'" class="right">'.$langs->trans("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')).' :</td>';
6589 //print '<td></td>';
6590 print '<td class="right">'.price($billedWithRetainedWarranty).'</td>';
6591 print '</tr>';
6592
6593 // retained warranty
6594 print '<tr>';
6595 print '<td colspan="'.($nbcols + 1).'" class="right">';
6596 print $langs->trans("RetainedWarranty").' ('.$object->retained_warranty.'%)';
6597 print !empty($object->retained_warranty_date_limit) ? ' '.$langs->trans("ToPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
6598 print '</td>';
6599 //print '<td></td>';
6600 print '<td class="right">'.price($retainedWarranty).'</td>';
6601 print '</tr>';
6602 }
6603 } else { // Credit note
6604 $resteapayeraffiche = $resteapayer;
6605 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6606 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6607
6608 // Total already paid back
6609 print '<tr>';
6610 print '<td colspan="'.($nbcols + 1).'" class="right">';
6611 print '<span class="opacitymedium">'.$langs->trans('AlreadyPaidBack').'</span>';
6612 print '</td>';
6613 //print '<td></td>';
6614 print '<td class="right"><span class="amount">'.price($sign * $totalpaid).'</span></td>';
6615 print '</tr>';
6616
6617 // Billed
6618 print '<tr>';
6619 print '<td colspan="'.($nbcols + 1).'" class="right"><span class="opacitymedium">'.$langs->trans("Billed").'</span></td>';
6620 //print '<td></td>';
6621 print '<td class="right">'.price($sign * $object->total_ttc).'</td>';
6622 print '</tr>';
6623
6624 // Remainder to pay back
6625 print '<tr><td colspan="'.($nbcols + 1).'" class="right">';
6626 print '<span class="opacitymedium">'.$langs->trans('RemainderToPayBack');
6627 if ($resteapayeraffiche > 0) {
6628 print ' ('.$langs->trans('NegativeIfExcessRefunded').')';
6629 }
6630 print '</span></td>';
6631 //print '<td class="nowrap"></td>';
6632 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopayback' : (' '.$cssforamountpaymentcomplete)).'">'.price($sign * $resteapayeraffiche);
6633 // TODO If credit not was converted into discount, we should show a tooltip to explain that remain to pay
6634 // is zero because already converted into discount for a future use, so no need to refund.
6635 print '</td>';
6636 print '</tr>';
6637
6638 // Remainder to pay back Multicurrency
6639 if (isModEnabled('multicurrency') && (($object->multicurrency_code && $object->multicurrency_code != $conf->currency) || $object->multicurrency_tx != 1)) {
6640 print '<tr>';
6641 print '<td colspan="'.($nbcols + 1).'" class="right">';
6642 print '<span class="opacitymedium">'.$langs->trans('RemainderToPayBackMulticurrency');
6643 if ($resteapayeraffiche > 0) {
6644 print ' ('.$langs->trans('NegativeIfExcessRefunded').')';
6645 }
6646 print '</span>';
6647 print '</td>';
6648 //print '<td></td>';
6649 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopaybacknoresize' : (' '.$cssforamountpaymentcompletenoresize)).'">'.price(price2num($sign * $object->multicurrency_tx * $resteapayeraffiche, 'MT'), 1, $langs, 1, -1, -1, (empty($object->multicurrency_code) ? $conf->currency : $object->multicurrency_code)).'</td>';
6650 print '</tr>';
6651 }
6652
6653 // Sold credit note
6654 // print '<tr><td colspan="'.$nbcols.'" class="right">'.$langs->trans('TotalTTC').' :</td>';
6655 // print '<td class="right" style="border: 1px solid;" bgcolor="#f0f0f0"><b>'.price($sign *
6656 // $object->total_ttc).'</b></td><td>&nbsp;</td></tr>';
6657 }
6658
6659 print '</table>';
6660 print '</div>';
6661
6662 // Margin Infos
6663 if (isModEnabled('margin')) {
6664 $formmargin->displayMarginInfos($object);
6665 }
6666
6667 print '</div>';
6668 print '</div>';
6669
6670 print '<div class="clearboth"></div><br>';
6671
6672 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
6673 $blocname = 'contacts';
6674 $title = $langs->trans('ContactsAddresses');
6675 include DOL_DOCUMENT_ROOT.'/core/tpl/bloc_showhide.tpl.php';
6676 }
6677
6678 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
6679 $blocname = 'notes';
6680 $title = $langs->trans('Notes');
6681 include DOL_DOCUMENT_ROOT.'/core/tpl/bloc_showhide.tpl.php';
6682 }
6683
6684 // Get object lines
6685 $result = $object->getLinesArray();
6686
6687 // Add products/services form
6688 //$forceall = 1;
6689 global $inputalsopricewithtax;
6690 $inputalsopricewithtax = 1;
6691
6692 // Show global modifiers for situation invoices
6693 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
6694 if ($object->situation_cycle_ref && $object->status == 0) {
6695 print '<!-- Area to change globally the situation percent -->'."\n";
6696 print '<div class="div-table-responsive-no-min">';
6697
6698 print '<form name="updatealllines" id="updatealllines" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'#updatealllines" method="POST">';
6699 print '<input type="hidden" name="token" value="'.newToken().'" />';
6700 print '<input type="hidden" name="action" value="updatealllines" />';
6701 print '<input type="hidden" name="id" value="'.$object->id.'" />';
6702 print '<input type="hidden" name="page_y" value="" />';
6703 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
6704
6705 print '<table id="tablelines_all_progress" class="noborder noshadow centpercent">';
6706
6707 print '<tr class="liste_titre nodrag nodrop">';
6708
6709 // Adds a line numbering column
6710 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
6711 print '<td align="center" width="5">&nbsp;</td>';
6712 }
6713 print '<td class="minwidth500imp">'.$langs->trans('ModifyAllLines').'</td>';
6714 print '<td class="right">'.$langs->trans('CumulativeProgression').'</td>';
6715 print '<td>&nbsp;</td>';
6716 print "</tr>\n";
6717
6718 print '<tr class="nodrag nodrop">';
6719 // Adds a line numbering column
6720 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
6721 print '<td align="center" width="5">&nbsp;</td>';
6722 }
6723 print '<td>&nbsp;</td>';
6724 print '<td class="nowrap right"><input type="text" size="1" value="" name="all_progress">%</td>';
6725 print '<td class="right"><input type="submit" class="button reposition small" name="all_percent" value="'.$langs->trans("Modify").'" /></td>';
6726 print '</tr>';
6727
6728 print '</table>';
6729
6730 print '</form>';
6731
6732 print '</div>';
6733 }
6734 }
6735
6736 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">
6737 <input type="hidden" name="token" value="' . newToken().'">
6738 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
6739 <input type="hidden" name="mode" value="">
6740 <input type="hidden" name="page_y" value="">
6741 <input type="hidden" name="id" value="' . $object->id.'">
6742 <input type="hidden" name="backtopage" value="'.$backtopage.'">
6743 ';
6744
6745 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
6746 if (isModEnabled('subtotals')) {
6747 include DOL_DOCUMENT_ROOT.'/core/tpl/subtotal_ajaxrow.tpl.php';
6748 } else {
6749 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
6750 }
6751 }
6752
6753 print '<div class="div-table-responsive-no-min">';
6754 print '<table id="tablelines" class="noborder noshadow centpercent nomarginbottom">';
6755
6756 // Show object lines
6757 if (!empty($object->lines)) {
6758 $object->printObjectLines($action, $mysoc, $soc, $lineid, 1);
6759 }
6760
6761 // Form to add new line
6762 if ($object->status == 0 && $usercancreate && $action != 'valid') {
6763 if ($action != 'editline' && $action != 'selectlines') {
6764 // Add free products/services
6765
6766 $parameters = array();
6767 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6768 if ($reshook < 0) {
6769 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
6770 }
6771 if (empty($reshook)) {
6772 $object->formAddObjectLine(1, $mysoc, $soc);
6773 }
6774 } else {
6775 $parameters = array();
6776 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6777 }
6778 }
6779
6780 print "</table>\n";
6781 print "</div>";
6782
6783 print "</form>\n";
6784 }
6785 print dol_get_fiche_end();
6786
6787
6788 // Actions buttons
6789
6790 if ($action != 'prerelance' && $action != 'presend' && $action != 'valid' && $action != 'editline') {
6791 print '<div class="tabsAction">';
6792
6793 $parameters = array();
6794 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6795 if (empty($reshook)) {
6796 $params = array(
6797 'attr' => array(
6798 'class' => 'classfortooltip',
6799 'title' => ''
6800 )
6801 );
6802 // Edit a validated invoice without any payment and not transferred to accounting
6803 if ($object->status == Facture::STATUS_VALIDATED && !getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
6804 // We check if lines of invoice are not already transferred into accountancy
6805 $ventilExportCompta = $object->getVentilExportCompta();
6806
6807 if ($ventilExportCompta == 0) {
6808 if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == price2num($object->total_ttc, 'MT', 1) && empty($object->paye))) {
6809 if (!$objectidnext && $object->is_last_in_cycle()) {
6810 if ($usercanunvalidate) {
6811 unset($params['attr']['title']);
6812 print dolGetButtonAction($langs->trans('Modify'), '', 'default', $_SERVER['PHP_SELF'] . '?facid=' . $object->id . '&action=modif&token=' . newToken(), '', true, $params);
6813 } else {
6814 $params['attr']['title'] = $langs->trans('NotEnoughPermissions');
6815 print dolGetButtonAction($langs->trans('Modify'), '', 'default', $_SERVER['PHP_SELF'] . '?facid=' . $object->id . '&action=modif&token=' . newToken(), '', false, $params);
6816 }
6817 } elseif (!$object->is_last_in_cycle()) {
6818 $params['attr']['title'] = $langs->trans('NotLastInCycle');
6819 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6820 } else {
6821 $params['attr']['title'] = $langs->trans('DisabledBecauseReplacedInvoice');
6822 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6823 }
6824 }
6825 } else {
6826 $params['attr']['title'] = $langs->trans('DisabledBecauseDispatchedInBookkeeping');
6827 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6828 }
6829 }
6830
6831 $discount = new DiscountAbsolute($db);
6832 $result = $discount->fetch(0, $object->id);
6833
6834 // Reopen an invoice
6836 || ($object->type == Facture::TYPE_CREDIT_NOTE && empty($discount->id))
6837 || ($object->type == Facture::TYPE_DEPOSIT && empty($discount->id))
6838 || ($object->type == Facture::TYPE_SITUATION && empty($discount->id)))
6839 && ($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED || ($object->status == 1 && $object->paye == 1)) // Condition ($object->status == 1 && $object->paye == 1) should not happened but can be found due to corrupted data
6840 && ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || $usercanreopen)) { // A paid invoice (partially or completely)
6841 if ($object->close_code != 'replaced' || (!$objectidnext)) { // Not replaced by another invoice or replaced but the replacement invoice has been deleted
6842 unset($params['attr']['title']);
6843 print dolGetButtonAction($langs->trans('ReOpen'), '', 'default', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=reopen&token='.newToken(), '', true, $params);
6844 } else {
6845 $params['attr']['title'] = $langs->trans("DisabledBecauseReplacedInvoice");
6846 print dolGetButtonAction($langs->trans('ReOpen'), '', 'default', '#', '', false, $params);
6847 }
6848 }
6849
6850 // Create contract
6851 if (getDolGlobalString('CONTRACT_CREATE_FROM_INVOICE')) {
6852 if (isModEnabled('contract') && $object->status == Facture::STATUS_VALIDATED) {
6853 $langs->load("contracts");
6854
6855 if ($usercancreatecontract) {
6856 print '<a class="butAction" href="' . DOL_URL_ROOT . '/contrat/card.php?action=create&origin=' . $object->element . '&originid=' . $object->id . '&socid=' . $object->socid . '">' . $langs->trans('AddContract') . '</a>';
6857 }
6858 }
6859 }
6860
6861 // Subtotal
6862 if ($object->status == Facture::STATUS_DRAFT && isModEnabled('subtotals')
6863 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_TEXT_'.strtoupper($object->element)))) {
6864 $langs->load("subtotals");
6865
6866 $url_button = array();
6867
6868 $url_button[] = array(
6869 'lang' => 'subtotals',
6870 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
6871 'perm' => (bool) $usercancreate,
6872 'label' => $langs->trans('AddTitleLine'),
6873 'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_title_line&token='.newToken()
6874 );
6875
6876 $url_button[] = array(
6877 'lang' => 'subtotals',
6878 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
6879 'perm' => (bool) $usercancreate,
6880 'label' => $langs->trans('AddSubtotalLine'),
6881 'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_subtotal_line&token='.newToken()
6882 );
6883
6884 $url_button[] = array(
6885 'lang' => 'subtotals',
6886 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TEXT_'.strtoupper($object->element))),
6887 'perm' => (bool) $usercancreate,
6888 'label' => $langs->trans('AddTextLine'),
6889 'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_text_line&token='.newToken()
6890 );
6891 print dolGetButtonAction('', $langs->trans('SubTotal'), 'default', $url_button, '', true);
6892 }
6893
6894 // Validate
6895 if ($object->status == Facture::STATUS_DRAFT && count($object->lines) > 0
6896 && ((($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA || $object->type == Facture::TYPE_SITUATION) && (getDolGlobalString('FACTURE_ENABLE_NEGATIVE') || $object->total_ttc >= 0))
6897 || ($object->type == Facture::TYPE_CREDIT_NOTE && $object->total_ttc <= 0))) {
6898 if ($usercanvalidate) {
6899 unset($params['attr']['title']);
6900 print dolGetButtonAction($langs->trans('Validate'), '', 'default', $_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=valid&token='.newToken(), '', true, $params);
6901 }
6902 }
6903
6904 // Send by mail
6905 if (empty($user->socid)) {
6906 if (($object->status == Facture::STATUS_VALIDATED || $object->status == Facture::STATUS_CLOSED) || getDolGlobalString('FACTURE_SENDBYEMAIL_FOR_ALL_STATUS')) {
6907 if ($objectidnext) {
6908 $params['attr']['title'] = $langs->trans("DisabledBecauseReplacedInvoice");
6909 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', '#', '', false, $params);
6910 } else {
6911 if ($usercansend) {
6912 unset($params['attr']['title']);
6913 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=presend&mode=init#formmailbeforetitle', '', true, $params);
6914 } else {
6915 unset($params['attr']['title']);
6916 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', '#', '', false, $params);
6917 }
6918 }
6919 }
6920 }
6921
6922 // Request a direct debit order
6923 if ($object->status > Facture::STATUS_DRAFT && $object->paye == 0) {
6924 if ($resteapayer > 0) {
6925 if ($usercancreatewithdrarequest) {
6926 if (!$objectidnext && $object->close_code != 'replaced') { // Not replaced by another invoice
6927 print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$object->id.'" title="'.dol_escape_htmltag($langs->trans("MakeWithdrawRequest")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
6928 } else {
6929 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('MakeWithdrawRequest').'</span>';
6930 }
6931 } else {
6932 //print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
6933 }
6934 } else {
6935 //print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("AmountMustBePositive")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
6936 }
6937 }
6938
6939 // POS Ticket
6940 if (isModEnabled('takepos') && $object->module_source == 'takepos') {
6941 $langs->load("cashdesk");
6942 $receipt_url = DOL_URL_ROOT."/takepos/receipt.php";
6943 print '<a target="_blank" rel="noopener noreferrer" class="butAction" href="'.$receipt_url.'?facid='.((int) $object->id).'">'.$langs->trans('POSTicket').'</a>';
6944 }
6945
6946 // Create payment
6947 if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->status == 1 && $object->paye == 0 && $usercanissuepayment) {
6948 if ($objectidnext) {
6949 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('DoPayment').'</span>';
6950 } else {
6951 if ($object->type == Facture::TYPE_DEPOSIT && $resteapayer == 0) {
6952 // For down payment, we refuse to receive more than amount to pay.
6953 $params['attr']['title'] = $langs->trans('DisabledBecauseRemainderToPayIsZero');
6954 print dolGetButtonAction($langs->trans('DoPayment'), '', 'default', '#', '', false, $params);
6955 } else {
6956 // Sometimes we can receive more, so we accept to enter more and will offer a button to convert into discount (but it is not a credit note, just a prepayment done)
6957 //print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/paiement.php?facid='.$object->id.'&action=create&accountid='.$object->fk_account.'">'.$langs->trans('DoPayment').'</a>';
6958 unset($params['attr']['title']);
6959 print dolGetButtonAction($langs->trans('DoPayment'), '', 'default', DOL_URL_ROOT.'/compta/paiement.php?facid='.$object->id.'&action=create'.($object->fk_account > 0 ? '&accountid='.$object->fk_account : ''), '', true, $params);
6960 }
6961 }
6962 }
6963
6964 $sumofpayment = $totalpaid;
6965 $sumofpaymentall = $totalpaid + $totalcreditnotes + $totaldeposits;
6966
6967 // Reverse back money or convert to reduction
6969 // For credit note only
6970 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $usercanissuepayment) {
6971 if ($resteapayer == 0) {
6972 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseRemainderToPayIsZero").'">'.$langs->trans('DoPaymentBack').'</span>';
6973 } else {
6974 print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/paiement.php?facid='.$object->id.'&action=create&accountid='.$object->fk_account.'">'.$langs->trans('DoPaymentBack').'</a>';
6975 }
6976 }
6977
6978 // For standard invoice with excess received
6979 if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_SITUATION) && $object->status == Facture::STATUS_VALIDATED && empty($object->paye) && $resteapayer < 0 && $usercancreate && empty($discount->id)) {
6980 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=converttoreduc&token='.newToken().'">'.$langs->trans('ConvertExcessReceivedToReduc').'</a>';
6981 }
6982 // For credit note
6983 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $usercancreate
6984 && (getDolGlobalString('INVOICE_ALLOW_REUSE_OF_CREDIT_WHEN_PARTIALLY_REFUNDED') || $sumofpayment == 0) && $object->total_ht < 0
6985 ) {
6986 print '<a class="butAction classfortooltip'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=converttoreduc&token='.newToken().'" title="'.dol_escape_htmltag($langs->trans("ConfirmConvertToReduc2")).'">'.$langs->trans('ConvertToReduc').'</a>';
6987 }
6988
6989 // For down payment invoice (deposit)
6990 if ($object->type == Facture::TYPE_DEPOSIT && $usercancreate && $object->status > Facture::STATUS_DRAFT && empty($discount->id)) {
6991 // We can close a down payment only if paid amount is same than amount of down payment (by definition). We can bypass this if hidden and unstable option DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID is set.
6992 if (price2num($object->total_ttc, 'MT') <= price2num($sumofpaymentall, 'MT') || getDolGlobalInt('DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID') || ($object->type == Facture::STATUS_ABANDONED && in_array($object->close_code, array('bankcharge', 'discount_vat', 'other')))) {
6993 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=converttoreduc&token='.newToken().'">'.$langs->trans('ConvertToReduc').'</a>';
6994 } else {
6995 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("AmountPaidMustMatchAmountOfDownPayment").'">'.$langs->trans('ConvertToReduc').'</span>';
6996 }
6997 }
6998 }
6999
7000 // Classify paid
7001 if ($object->status == Facture::STATUS_VALIDATED && $usercanissuepayment && (
7002 ($object->type != Facture::TYPE_CREDIT_NOTE && $object->type != Facture::TYPE_DEPOSIT && ($resteapayer <= 0 || (getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') && $object->total_ttc == $resteapayer))) ||
7003 ($object->type == Facture::TYPE_CREDIT_NOTE && $resteapayer >= 0) ||
7004 ($object->type == Facture::TYPE_DEPOSIT && $object->total_ttc > 0)
7005 )
7006 ) {
7007 if ($object->type == Facture::TYPE_DEPOSIT && price2num($object->total_ttc, 'MT') != price2num($sumofpaymentall, 'MT')) {
7008 // We can close a down payment only if paid amount is same than amount of down payment (by definition)
7009 $params['attr']['title'] = $langs->trans('AmountPaidMustMatchAmountOfDownPayment');
7010 print dolGetButtonAction($langs->trans('ClassifyPaid'), '', 'default', '#', '', false, $params);
7011 } else {
7012 unset($params['attr']['title']);
7013 print dolGetButtonAction($langs->trans('ClassifyPaid'), '', 'default', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=paid&token='.newToken(), '', true, $params);
7014 }
7015 }
7016
7017 // Classify 'closed not completely paid' (possible if validated and not yet set as paid completely)
7018 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $resteapayer > 0 && (!getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') || $resteapayer != $object->total_ttc) && $usercanissuepayment) {
7019 if ($totalpaid > 0 || $totalcreditnotes > 0) {
7020 // If one payment or one credit note was linked to this invoice
7021 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=paid&token='.newToken().'">'.$langs->trans('ClassifyPaidPartially').'</a>';
7022 } else {
7023 if (!getDolGlobalString('INVOICE_CAN_NEVER_BE_CANCELED')) {
7024 if ($objectidnext) {
7025 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('ClassifyCanceled').'</span>';
7026 } else {
7027 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=canceled&token='.newToken().'">'.$langs->trans('ClassifyCanceled').'</a>';
7028 }
7029 }
7030 }
7031 }
7032
7033 // Create next situation invoice
7034 if ($usercancreate && $object->isSituationInvoice() && ($object->status == 1 || $object->status == 2)) {
7035 if ($object->is_last_in_cycle() && $object->situation_final != 1) {
7036 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=create&type=5&origin=facture&originid='.$object->id.'&socid='.$object->socid.'" >'.$langs->trans('CreateNextSituationInvoice').'</a>';
7037 } elseif (!$object->is_last_in_cycle()) {
7038 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseNotLastInCycle").'">'.$langs->trans('CreateNextSituationInvoice').'</a>';
7039 } else {
7040 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseFinal").'">'.$langs->trans('CreateNextSituationInvoice').'</a>';
7041 }
7042 }
7043
7044 // Create a credit note
7045 if (($object->type == Facture::TYPE_STANDARD || ($object->type == Facture::TYPE_DEPOSIT && !getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) || $object->type == Facture::TYPE_PROFORMA) && $object->status > 0 && $usercancreate) {
7046 if (!$objectidnext) {
7047 print '<!-- button create credit note -->';
7048 if ($object->module_source == 'takepos') {
7049 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseInvoiceGeneratedBy", $langs->transnoentitiesnoconv('TakePOS')).'">'.$langs->trans("CreateCreditNote").'</a>';
7050 } else {
7051 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?socid='.$object->socid.'&fac_avoir='.$object->id.'&action=create&type=2'.($object->fk_project > 0 ? '&projectid='.$object->fk_project : '').($object->entity > 0 ? '&originentity='.$object->entity : '').'">'.$langs->trans("CreateCreditNote").'</a>';
7052 }
7053 }
7054 }
7055
7056 // For situation invoice, create credit note
7057 if ($object->status > Facture::STATUS_DRAFT
7058 && $object->isSituationInvoice()
7059 && $usercancreate
7060 && !$objectidnext
7061 && $object->is_last_in_cycle()
7062 && getDolGlobalInt('INVOICE_USE_SITUATION_CREDIT_NOTE')
7063 ) {
7064 if ($usercanunvalidate) {
7065 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?socid='.$object->socid.'&fac_avoir='.$object->id.'&invoiceAvoirWithLines=1&action=create&type=2'.($object->fk_project > 0 ? '&projectid='.$object->fk_project : '').'">'.$langs->trans("CreateCreditNote").'</a>';
7066 } else {
7067 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans("CreateCreditNote").'</span>';
7068 }
7069 }
7070
7071 // Clone as predefined / Create template
7072 if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA) && $object->status == 0 && $usercancreate) {
7073 if (!$objectidnext && count($object->lines) > 0) {
7074 unset($params['attr']['title']);
7075 print dolGetButtonAction($langs->trans('ChangeIntoRepeatableInvoice'), '', 'default', DOL_URL_ROOT.'/compta/facture/card-rec.php?facid='.$object->id.'&action=create', '', true, $params);
7076 }
7077 }
7078
7079 // Clone
7080 if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA) && $usercancreate) {
7081 unset($params['attr']['title']);
7082 print dolGetButtonAction($langs->trans('ToClone'), '', 'clone', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=clone&object=invoice&token='.newToken(), '', true, $params);
7083 }
7084
7085 // Remove situation from cycle
7086 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
7087 && $object->isSituationInvoice()
7088 && $usercancreate
7089 && !$objectidnext
7090 && $object->situation_counter > 1
7091 && $object->is_last_in_cycle()
7092 && $usercanunvalidate
7093 ) {
7094 if (price2num($object->total_ttc - $totalcreditnotes, 'MT') == 0) {
7095 print '<a id="butSituationOut" class="butAction" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=situationout&token='.newToken().'">'.$langs->trans("RemoveSituationFromCycle").'</a>';
7096 } else {
7097 print '<a id="butSituationOutRefused" class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseNotEnouthCreditNote").'" >'.$langs->trans("RemoveSituationFromCycle").'</a>';
7098 }
7099 }
7100
7101 // Delete
7102 $isErasable = $object->is_erasable();
7103
7104 $htmltooltip = '';
7105 if ($isErasable == -6) {
7106 $htmltooltip = $langs->trans('DisabledBecauseAlreadySentByEmail');
7107 } elseif ($isErasable == -5) {
7108 $htmltooltip = $langs->trans('DisabledBecauseAlreadyPrintedOnce');
7109 } elseif ($isErasable == -4) {
7110 $htmltooltip = $langs->trans('DisabledBecausePayments');
7111 } elseif ($isErasable == -3) {
7112 $htmltooltip = $langs->trans('DisabledBecauseNotLastSituationInvoice');
7113 } elseif ($isErasable == -2) {
7114 $htmltooltip = $langs->trans('DisabledBecauseNotLastInvoice');
7115 } elseif ($isErasable == -1) {
7116 $htmltooltip = $langs->trans('DisabledBecauseDispatchedInBookkeeping');
7117 } elseif ($isErasable <= 0) { // Any other cases
7118 $htmltooltip = $langs->trans('DisabledBecauseNotErasable').(empty($object->error) ? ': UnknownReason' : ': '.$object->error);
7119 } elseif ($objectidnext) {
7120 $htmltooltip = $langs->trans('DisabledBecauseReplacedInvoice');
7121 }
7122 if ($usercandelete || ($usercancreate && $isErasable == 1)) { // isErasable = 1 means draft with temporary ref (draft can always be deleted with no need of permissions)
7123 $enableDelete = false;
7124 $deleteHref = '#';
7125 if ($isErasable > 0 && ! $objectidnext) {
7126 $deleteHref = $_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=delete&token='.newToken();
7127 $enableDelete = true;
7128 }
7129 unset($params['attr']['title']);
7130 print dolGetButtonAction($htmltooltip, $langs->trans('Delete'), 'delete', $deleteHref, '', $enableDelete, $params);
7131 } else {
7132 unset($params['attr']['title']);
7133 print dolGetButtonAction($htmltooltip, $langs->trans('Delete'), 'delete', '#', '', false);
7134 }
7135 }
7136 print '</div>';
7137 }
7138
7139 // Select mail models is same action as presend
7140 if (GETPOST('modelselected', 'alpha')) {
7141 $action = 'presend';
7142 }
7143 if ($action != 'prerelance' && $action != 'presend') {
7144 print '<div class="fichecenter"><div class="fichehalfleft">';
7145 print '<a name="builddoc"></a>'; // ancre
7146
7147 // Generated documents
7148 $filename = dol_sanitizeFileName($object->ref);
7149 $filedir = $conf->invoice->multidir_output[$object->entity ?? $conf->entity].'/'.dol_sanitizeFileName($object->ref);
7150 $urlsource = $_SERVER['PHP_SELF'].'?facid='.$object->id;
7151 $genallowed = $usercanread;
7152 $delallowed = $usercancreate;
7153 $tooltipAfterComboOfModels = '';
7154 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_INVOICE')) {
7155 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_INVOICE_TERMSOFSALE'));
7156 }
7157
7158 print $formfile->showdocuments(
7159 'facture',
7160 $filename,
7161 $filedir,
7162 $urlsource,
7163 $genallowed,
7164 (int) $delallowed,
7165 $object->model_pdf,
7166 1,
7167 0,
7168 0,
7169 28,
7170 0,
7171 '',
7172 '',
7173 '',
7174 $soc->default_lang,
7175 '',
7176 $object,
7177 0,
7178 'remove_file_comfirm',
7179 $tooltipAfterComboOfModels
7180 );
7181
7182 $somethingshown = $formfile->numoffiles;
7183
7184 // Show links to link elements
7185 $tmparray = $form->showLinkToObjectBlock($object, array(), array('invoice'), 1);
7186 $linktoelem = $tmparray['linktoelem'];
7187 $htmltoenteralink = $tmparray['htmltoenteralink'];
7188 print $htmltoenteralink;
7189
7190 $compatibleImportElementsList = false;
7191 if ($usercancreate
7192 && $object->status == Facture::STATUS_DRAFT
7194 $compatibleImportElementsList = array('commande', 'propal', 'subscription'); // import from linked elements
7195 }
7196 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
7197
7198 // Show online payment link
7199 // The list can be complete by the hook 'doValidatePayment' executed inside getValidOnlinePaymentMethods()
7200 include_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7201 $validpaymentmethod = getValidOnlinePaymentMethods('');
7202 $useonlinepayment = count($validpaymentmethod);
7203
7204 if ($object->status != Facture::STATUS_DRAFT && $useonlinepayment) {
7205 print '<br><!-- Link to pay -->'."\n";
7206 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7207 print showOnlinePaymentUrl('invoice', $object->ref).'<br>';
7208 }
7209
7210 print '</div><div class="fichehalfright">';
7211
7212 $MAXEVENT = 10;
7213
7214 $morehtmlcenter = '<div class="nowraponall">';
7215 $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT.'/compta/facture/messaging.php?id='.$object->id);
7216 $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullList'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/compta/facture/agenda.php?id='.$object->id);
7217 $morehtmlcenter .= '</div>';
7218
7219 // List of actions on element
7220 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
7221 $formactions = new FormActions($db);
7222 $somethingshown = $formactions->showactions($object, 'invoice', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
7223
7224 print '</div></div>';
7225 }
7226
7227
7228 // Presend form
7229 $modelmail = 'facture_send';
7230 $defaulttopic = 'SendBillRef';
7231 $diroutput = $conf->invoice->multidir_output[$object->entity ?? $conf->entity];
7232 $trackid = 'inv'.$object->id;
7233
7234 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
7235}
7236
7237// End of page
7238llxFooter();
7239$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage bank accounts.
Class to manage accounting journals.
Class to manage absolute discounts.
Class to manage a WYSIWYG editor.
Class to manage warehouses.
Class to manage standard extra fields.
Class to manage invoices.
const TYPE_REPLACEMENT
Replacement invoice.
const STATUS_DRAFT
Draft status.
const TYPE_STANDARD
Standard invoice.
const TYPE_SITUATION
Situation invoice.
const TYPE_PROFORMA
Proforma invoice (should not be used.
const STATUS_VALIDATED
Validated (need to be paid)
const TYPE_DEPOSIT
Deposit invoice.
const STATUS_ABANDONED
Classified abandoned and no payment done.
const TYPE_CREDIT_NOTE
Credit note invoice.
const STATUS_CLOSED
Classified paid.
Class to manage invoice lines.
Class to manage invoice templates.
Class to manage building of HTML components.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class to manage "other" html components Only common components are here.
Class to help generate other html components Only common components are here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage building of HTML components.
static liste_modeles($db, $maxfilenamelength=0)
Return list of active generation modules.
Class to manage the table of subscription to notifications.
Class to manage payments of customer invoices.
Class ProductCombination Used to represent the relation between a product and one of its variants.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage VAT - Value-added tax (also known in French as TVA)
Definition tva.class.php:39
Class to manage Dolibarr users.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
global $mysoc
dol_get_last_hour($date, $gm='tzserver')
Return GMT time for last hour of a given GMT date (it replaces hours, min and second part to 23:59:59...
Definition date.lib.php:651
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:126
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition date.lib.php:436
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.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
GETPOSTDATE($prefix, $hourTime='', $gm='auto', $saverestore='')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
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.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
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...
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
getDictionaryValue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
Return the value of a filed into a dictionary for the record $id.
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
Definition html.lib.php:519
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition html.lib.php:717
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
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...
Definition html.lib.php:172
facture_prepare_head($object)
Initialize the array of tabs for customer invoice.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $notused, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.