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 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 *
26 * This program is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation; either version 3 of the License, or
29 * (at your option) any later version.
30 *
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
35 *
36 * You should have received a copy of the GNU General Public License
37 * along with this program. If not, see <https://www.gnu.org/licenses/>.
38 */
39
46// Load Dolibarr environment
47require '../../main.inc.php';
57require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
58require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
59require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
60require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
61require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
62require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
63require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
64require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
65require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
66require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
67require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
68require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php';
69require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
70require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
71if (isModEnabled('order')) {
72 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
73}
74if (isModEnabled('project')) {
75 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
76 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
77}
78require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
79
80if (isModEnabled('variants')) {
81 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
82}
83if (isModEnabled('accounting')) {
84 require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
85}
86
87// Load translation files required by the page
88$langs->loadLangs(array('bills', 'companies', 'compta', 'products', 'banks', 'main', 'withdrawals'));
89if (isModEnabled('incoterm')) {
90 $langs->load('incoterm');
91}
92if (isModEnabled('margin')) {
93 $langs->load('margins');
94}
95
96// General $Variables
97$action = GETPOST('action', 'aZ09');
98$confirm = GETPOST('confirm', 'alpha');
99$cancel = GETPOST('cancel', 'alpha');
100$backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used
101$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used
102
103$id = (GETPOSTINT('id') ? GETPOSTINT('id') : GETPOSTINT('facid')); // For backward compatibility
104$ref = GETPOST('ref', 'alpha');
105$socid = GETPOSTINT('socid');
106$lineid = GETPOSTINT('lineid');
107$origin = GETPOST('origin', 'alpha');
108$originid = (GETPOSTINT('originid') ? GETPOSTINT('originid') : GETPOSTINT('origin_id')); // For backward compatibility
109$fac_rec = GETPOSTINT('fac_rec');
110$facid = GETPOSTINT('facid');
111$ref_client = GETPOST('ref_client', 'alpha');
112$inputReasonId = GETPOSTINT('input_reason_id');
113$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
114$projectid = (GETPOSTINT('projectid') ? GETPOSTINT('projectid') : 0);
115$selectedLines = GETPOST('toselect', 'array:int');
116
117// PDF
118$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
119$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
120$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
121
122// Number of lines for predefined product/service choices
123$NBLINES = 4;
124
125$object = new Facture($db);
126
127// Fetch optionals attributes and labels
128$extrafields->fetch_name_optionals_label($object->table_element);
129
130$objectidnext = 0;
131$total_global_ttc = 0;
132$displayWarranty = false;
133$statusreplacement = 0;
134$type_fac = 0;
135$price_base_type = '';
136$array_options = array();
137
138// Load object
139if ($id > 0 || !empty($ref)) {
140 if ($action != 'add') {
141 if (!getDolGlobalString('INVOICE_USE_SITUATION')) {
142 $fetch_situation = false;
143 } else {
144 $fetch_situation = true;
145 }
146 $ret = $object->fetch($id, $ref, '', 0, $fetch_situation);
147 if ($ret > 0 && isset($object->fk_project)) {
148 $ret = $object->fetchProject();
149 }
150 }
151}
152
153// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
154$hookmanager->initHooks(array('invoicecard', 'globalcard'));
155
156// Permissions
157$usercanread = $user->hasRight("facture", "lire");
158$usercancreate = $user->hasRight("facture", "creer");
159$usercanissuepayment = $user->hasRight("facture", "paiement");
160$usercandelete = $user->hasRight("facture", "supprimer") || ($usercancreate && isset($object->status) && $object->status == $object::STATUS_DRAFT);
161$usercancreatecontract = $user->hasRight("contrat", "creer");
162
163// Advanced Permissions
164$usercanvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'validate')));
165$usercansend = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercanread) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'send')));
166$usercanreopen = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'reopen')));
167if (getDolGlobalString('INVOICE_DISALLOW_REOPEN')) {
168 $usercanreopen = false;
169}
170$usercanunvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !empty($usercancreate)) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'unvalidate')));
171$usermustrespectpricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
172
173// Other permissions
174$usercancreatemargin = $user->hasRight('margins', 'creer');
175$usercanreadallmargin = $user->hasRight('margins', 'liretous');
176$usercancreatewithdrarequest = $user->hasRight('prelevement', 'bons', 'creer');
177
178$permissionnote = $usercancreate; // Used by the include of actions_setnotes.inc.php
179$permissiondellink = $usercancreate; // Used by the include of actions_dellink.inc.php
180$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdonw.inc.php
181$permissiontoadd = $usercancreate; // Used by the include of actions_addupdatedelete.inc.php
182$permissiontoeditextra = $usercancreate;
183if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
184 // For action 'update_extras', is there a specific permission set for the attribute to update
185 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
186}
187
188// retained warranty invoice available type
189$retainedWarrantyInvoiceAvailableType = array();
190if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
191 $retainedWarrantyInvoiceAvailableType = explode('+', getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY'));
192}
193
194// Security check
195if ($user->socid) {
196 $socid = $user->socid;
197}
198$isdraft = (($object->status == Facture::STATUS_DRAFT) ? 1 : 0);
199
200$result = restrictedArea($user, 'facture', $object->id, '', '', 'fk_soc', 'rowid', $isdraft);
201
202
203/*
204 * Actions
205 */
206
207$error = 0;
208
209$parameters = array('socid' => $socid);
210$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
211if ($reshook < 0) {
212 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
213}
214
215if (empty($reshook)) {
216 $backurlforlist = dolBuildUrl(DOL_URL_ROOT.'/compta/facture/list.php');
217
218 if (empty($backtopage) || ($cancel && empty($id))) {
219 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
220 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
221 $backtopage = $backurlforlist;
222 } else {
223 $backtopage = dolBuildUrl(DOL_URL_ROOT.'/compta/facture/card.php', ['id' => ((!empty($id) && $id > 0) ? $id : '__ID__')]);
224 }
225 }
226 }
227
228 if ($cancel) {
229 if (!empty($backtopageforcancel)) {
230 header("Location: ".$backtopageforcancel);
231 exit;
232 } elseif (!empty($backtopage)) {
233 header("Location: ".$backtopage);
234 exit;
235 }
236 $action = '';
237 }
238
239 include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be 'include', not 'include_once'
240
241 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
242
243 include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
244
245 // Action clone object
246 if ($action == 'confirm_clone' && $confirm == 'yes' && $permissiontoadd) {
247 if (!($socid > 0)) {
248 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('IdThirdParty')), null, 'errors');
249 } else {
250 $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.
251 '@phan-var-force Facture $objectutil';
252
253 $objectutil->date = dol_mktime(12, 0, 0, GETPOSTINT('newdatemonth'), GETPOSTINT('newdateday'), GETPOSTINT('newdateyear'));
254 $objectutil->socid = $socid;
255 $result = $objectutil->createFromClone($user, $id, (GETPOSTISSET('entity') ? GETPOSTINT('entity') : null));
256 if ($result > 0) {
257 $warningMsgLineList = array();
258 // check all product lines are to sell otherwise add a warning message for each product line is not to sell
259 foreach ($objectutil->lines as $line) {
260 if (!is_object($line->product)) {
261 $line->fetch_product();
262 }
263 if (is_object($line->product) && $line->product->id > 0) {
264 if (empty($line->product->status)) {
265 $warningMsgLineList[$line->id] = $langs->trans('WarningLineProductNotToSell', $line->product->ref);
266 }
267 }
268 }
269 if (!empty($warningMsgLineList)) {
270 setEventMessages('', $warningMsgLineList, 'warnings');
271 }
272
273 header("Location: " . $_SERVER['PHP_SELF'] . '?facid=' . $result);
274 exit();
275 } else {
276 $langs->load("errors");
277 setEventMessages($objectutil->error, $objectutil->errors, 'errors');
278 $action = '';
279 }
280 }
281 } elseif ($action == 'reopen' && $usercanreopen) {
282 $result = $object->fetch($id);
283
284 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
285 $result = $object->setUnpaid($user);
286 if ($result > 0) {
287 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
288 exit();
289 } else {
290 setEventMessages($object->error, $object->errors, 'errors');
291 }
292 }
293 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) {
294 // Delete invoice
295 $result = $object->fetch($id);
296 $object->fetch_thirdparty();
297
298 $idwarehouse = GETPOST('idwarehouse');
299
300 $qualified_for_stock_change = 0;
301 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
302 $qualified_for_stock_change = $object->hasProductsOrServices(2);
303 } else {
304 $qualified_for_stock_change = $object->hasProductsOrServices(1);
305 }
306
307 $isErasable = $object->is_erasable();
308
309 if (($isErasable > 0) || ($usercancreate && $isErasable == 1)) {
310 $result = $object->delete($user, 0, (int) $idwarehouse);
311 if ($result > 0) {
312 header('Location: '.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1');
313 exit();
314 } else {
315 setEventMessages($object->error, $object->errors, 'errors');
316 $action = '';
317 }
318 }
319 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
320 // Delete line
321 $object->fetch($id);
322 $object->fetch_thirdparty();
323
324 $result = $object->deleteLine(GETPOSTINT('lineid'));
325 if ($result > 0) {
326 // reorder lines
327 $object->line_order(true);
328 // Define output language
329 $outputlangs = $langs;
330 $newlang = '';
331 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
332 $newlang = GETPOST('lang_id');
333 }
334 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
335 $newlang = $object->thirdparty->default_lang;
336 }
337 if (!empty($newlang)) {
338 $outputlangs = new Translate("", $conf);
339 $outputlangs->setDefaultLang($newlang);
340 $outputlangs->load('products');
341 }
342 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
343 $ret = $object->fetch($id); // Reload to get new records
344 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
345 }
346 if ($result >= 0) {
347 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
348 exit();
349 }
350 } else {
351 setEventMessages($object->error, $object->errors, 'errors');
352 $action = '';
353 }
354 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) {
355 // Delete line
356 $object->fetch($id);
357 $object->fetch_thirdparty();
358
359 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'));
360 if ($result > 0) {
361 // reorder lines
362 $object->line_order(true);
363 // Define output language
364 $outputlangs = $langs;
365 $newlang = '';
366 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
367 $newlang = GETPOST('lang_id');
368 }
369 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
370 $newlang = $object->thirdparty->default_lang;
371 }
372 if (!empty($newlang)) {
373 $outputlangs = new Translate("", $conf);
374 $outputlangs->setDefaultLang($newlang);
375 $outputlangs->load('products');
376 }
377 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
378 $ret = $object->fetch($id); // Reload to get new records
379 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
380 }
381 if ($result >= 0) {
382 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
383 exit();
384 }
385 } else {
386 setEventMessages($object->error, $object->errors, 'errors');
387 $action = '';
388 }
389 } elseif ($action == 'unlinkdiscount' && $usercancreate) {
390 // Delete link of credit note to invoice
391 $discount = new DiscountAbsolute($db);
392 $result = $discount->fetch(GETPOSTINT("discountid"));
393 $discount->unlink_invoice();
394 } elseif ($action == 'valid' && $usercancreate) {
395 // Validation
396 $object->fetch($id);
397
398 if ((preg_match('/^[\‍(]?PROV/i', $object->ref) || empty($object->ref)) && // empty should not happened, but when it occurs, the test save life
399 getDolGlobalString('FAC_FORCE_DATE_VALIDATION') // If option enabled, we force invoice date
400 ) {
401 $object->date = dol_now();
402 }
403
404 if (getDolGlobalString('INVOICE_CHECK_POSTERIOR_DATE')) {
405 $last_of_type = $object->willBeLastOfSameType(true);
406 if (empty($object->date_validation) && !$last_of_type[0]) {
407 setEventMessages($langs->transnoentities("ErrorInvoiceIsNotLastOfSameType", $object->ref, dol_print_date($object->date, 'day'), dol_print_date($last_of_type[1], 'day')), null, 'errors');
408 $action = '';
409 }
410 }
411
412 // We check invoice sign
413 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
414 // If a credit note, the sign must be negative
415 if ($object->total_ht > 0) {
416 setEventMessages($langs->trans("ErrorInvoiceAvoirMustBeNegative"), null, 'errors');
417 $action = '';
418 }
419 } else {
420 // If not a credit note, amount with tax must be positive or nul.
421 // Note that amount excluding tax can be negative because you can have a invoice of 100 with vat of 20 that
422 // consumes a credit note of 100 with vat 0 (total with tax is 0 but without tax is -20).
423 // For some cases, credit notes can have a vat of 0 (for example when selling goods in France).
424 if (!getDolGlobalString('FACTURE_ENABLE_NEGATIVE') && $object->total_ttc < 0) {
425 setEventMessages($langs->trans("ErrorInvoiceOfThisTypeMustBePositive"), null, 'errors');
426 $action = '';
427 }
428
429 // Also negative lines should not be allowed on 'non Credit notes' invoices. A test is done when adding or updating lines but we must
430 // do it again in validation to avoid cases where invoice is created from another object that allow negative lines.
431 // 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
432 // when converted into 'available credit' and we will get a positive available credit line.
433 // 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).
434 $array_of_total_ht_per_vat_rate = array();
435 $array_of_total_ht_devise_per_vat_rate = array();
436 foreach ($object->lines as $line) {
437 //$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.
438 $vat_src_code_for_line = '';
439 if (empty($array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
440 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
441 }
442 if (empty($array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
443 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
444 }
445 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->total_ht;
446 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->multicurrency_total_ht;
447 }
448
449 //var_dump($array_of_total_ht_per_vat_rate);exit;
450 foreach ($array_of_total_ht_per_vat_rate as $vatrate => $tmpvalue) {
451 $tmp_total_ht = price2num($array_of_total_ht_per_vat_rate[$vatrate]);
452 $tmp_total_ht_devise = price2num($array_of_total_ht_devise_per_vat_rate[$vatrate]);
453
454 if (($tmp_total_ht < 0 || $tmp_total_ht_devise < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
455 if ($object->type == $object::TYPE_DEPOSIT) {
456 $langs->load("errors");
457 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
458 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
459 $error++;
460 $action = '';
461 } else {
462 $tmpvatratetoshow = explode('_', $vatrate);
463 $tmpvatratetoshow[0] = round((float) $tmpvatratetoshow[0], 2);
464
465 if ($tmpvatratetoshow[0] != 0) {
466 $langs->load("errors");
467 setEventMessages($langs->trans("ErrorLinesCantBeNegativeForOneVATRate", (string) $tmpvatratetoshow[0]), null, 'errors');
468 $error++;
469 $action = '';
470 }
471 }
472 }
473 }
474 }
475 } elseif ($action == 'classin' && $usercancreate) {
476 $object->fetch($id);
477 $object->setProject(GETPOSTINT('projectid'));
478 if ($result < 0) {
479 setEventMessages($object->error, $object->errors, 'errors');
480 }
481 } elseif ($action == 'setposinfo' && $usercancreate) {
482 $object->fetch($id);
483 $object->module_source = GETPOST('posmodule');
484 $object->pos_source = GETPOST('posterminal');
485 $result = $object->update($user);
486 if ($result < 0) {
487 setEventMessages($object->error, $object->errors, 'errors');
488 }
489 } elseif ($action == 'setmode' && $usercancreate) {
490 $object->fetch($id);
491 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
492 if ($result < 0) {
493 setEventMessages($object->error, $object->errors, 'errors');
494 }
495 } elseif ($action == 'setretainedwarrantyconditions' && $usercancreate) {
497 $object->fetch($id);
498 $object->retained_warranty_fk_cond_reglement = 0; // To clean property
499 $result = $object->setRetainedWarrantyPaymentTerms(GETPOSTINT('retained_warranty_fk_cond_reglement'));
500 if ($result < 0) {
501 setEventMessages($object->error, $object->errors, 'errors');
502 }
503
504 $old_rw_date_lim_reglement = $object->retained_warranty_date_limit;
505 $new_rw_date_lim_reglement = $object->calculate_date_lim_reglement($object->retained_warranty_fk_cond_reglement);
506 if ($new_rw_date_lim_reglement > $old_rw_date_lim_reglement) {
507 $object->retained_warranty_date_limit = $new_rw_date_lim_reglement;
508 }
509 if ($object->retained_warranty_date_limit < $object->date) {
510 $object->retained_warranty_date_limit = $object->date;
511 }
512 $result = $object->update($user);
513 if ($result < 0) {
514 setEventMessages($object->error, $object->errors, 'errors');
515 }
516 } elseif ($action == 'setretainedwarranty' && $usercancreate) {
517 $object->fetch($id);
518 $result = $object->setRetainedWarranty(GETPOSTFLOAT('retained_warranty'));
519 if ($result < 0) {
520 setEventMessages($object->error, $object->errors, 'errors');
521 }
522 } elseif ($action == 'setretainedwarrantydatelimit' && $usercancreate) {
523 $object->fetch($id);
524 $result = $object->setRetainedWarrantyDateLimit(GETPOSTDATE('retained_warranty_date_limit'));
525 if ($result < 0) {
526 setEventMessages($object->error, $object->errors, 'errors');
527 }
528 } elseif ($action == 'setmulticurrencycode' && $usercancreate) { // Multicurrency Code
529 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
530 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) { // Multicurrency rate
531 $result = $object->setMulticurrencyRate(GETPOSTFLOAT('multicurrency_tx'), GETPOSTINT('calculation_mode'));
532 } elseif ($action == 'setinvoicedate' && $usercancreate) {
533 $object->fetch($id);
534 $old_date_lim_reglement = $object->date_lim_reglement;
535 $newdate = dol_mktime(0, 0, 0, GETPOSTINT('invoicedatemonth'), GETPOSTINT('invoicedateday'), GETPOSTINT('invoicedateyear'), 'tzserver');
536 if (empty($newdate)) {
537 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
538 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id.'&action=editinvoicedate&token='.newToken());
539 exit;
540 }
541 if ($newdate > (dol_now('tzuserrel') + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
542 if (!getDolGlobalString('INVOICE_MAX_FUTURE_DELAY')) {
543 setEventMessages($langs->trans("WarningInvoiceDateInFuture"), null, 'warnings');
544 } else {
545 setEventMessages($langs->trans("WarningInvoiceDateTooFarInFuture"), null, 'warnings');
546 }
547 }
548
549 $object->date = $newdate;
550 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
551 if ($new_date_lim_reglement) {
552 $object->date_lim_reglement = $new_date_lim_reglement;
553 }
554 if ($object->date_lim_reglement < $object->date) {
555 $object->date_lim_reglement = $object->date;
556 }
557 $result = $object->update($user);
558 if ($result < 0) {
559 setEventMessages($object->error, $object->errors, 'errors');
560 $action = 'editinvoicedate';
561 }
562 } elseif ($action == 'setdate_pointoftax' && $usercancreate) {
563 $object->fetch($id);
564
565 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
566
567 $object->date_pointoftax = $date_pointoftax;
568 $result = $object->update($user);
569 if ($result < 0) {
570 setEventMessages($object->error, $object->errors, 'errors');
571 }
572 } elseif ($action == 'setconditions' && $usercancreate) {
573 $object->fetch($id);
574 $object->cond_reglement_code = 0; // To clean property
575 $object->cond_reglement_id = 0; // To clean property
576
577 $db->begin();
578
579 if (!$error) {
580 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'));
581 if ($result < 0) {
582 $error++;
583 setEventMessages($object->error, $object->errors, 'errors');
584 }
585 }
586
587 if (!$error) {
588 $old_date_lim_reglement = $object->date_lim_reglement;
589 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
590 if ($new_date_lim_reglement) {
591 $object->date_lim_reglement = $new_date_lim_reglement;
592 }
593 if ($object->date_lim_reglement < $object->date) {
594 $object->date_lim_reglement = $object->date;
595 }
596 $result = $object->update($user);
597 if ($result < 0) {
598 $error++;
599 setEventMessages($object->error, $object->errors, 'errors');
600 }
601 }
602
603 if ($error) {
604 $db->rollback();
605 } else {
606 $db->commit();
607 }
608 } elseif ($action == 'setpaymentterm' && $usercancreate) {
609 $object->fetch($id);
610 $object->date_lim_reglement = dol_mktime(12, 0, 0, GETPOSTINT('paymenttermmonth'), GETPOSTINT('paymenttermday'), GETPOSTINT('paymenttermyear'));
611 if ($object->date_lim_reglement < $object->date) {
612 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
613 setEventMessages($langs->trans("DatePaymentTermCantBeLowerThanObjectDate"), null, 'warnings');
614 }
615 $result = $object->update($user);
616 if ($result < 0) {
617 setEventMessages($object->error, $object->errors, 'errors');
618 }
619 } elseif ($action == 'setrevenuestamp' && $usercancreate) {
620 $object->fetch($id);
621 $object->revenuestamp = (float) price2num(GETPOST('revenuestamp'));
622 $result = $object->update($user);
623 $object->update_price(1);
624 if ($result < 0) {
625 setEventMessages($object->error, $object->errors, 'errors');
626 } else {
627 // Define output language
628 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
629 $outputlangs = $langs;
630 $newlang = '';
631 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
632 $newlang = GETPOST('lang_id', 'aZ09');
633 }
634 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
635 $newlang = $object->thirdparty->default_lang;
636 }
637 if (!empty($newlang)) {
638 $outputlangs = new Translate("", $conf);
639 $outputlangs->setDefaultLang($newlang);
640 $outputlangs->load('products');
641 }
642 $model = $object->model_pdf;
643 $ret = $object->fetch($id); // Reload to get new records
644
645 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
646 if ($result < 0) {
647 setEventMessages($object->error, $object->errors, 'errors');
648 }
649 }
650 }
651 } elseif ($action == 'set_incoterms' && isModEnabled('incoterm') && $usercancreate) { // Set incoterm
652 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
653 if ($result < 0) {
654 setEventMessages($object->error, $object->errors, 'errors');
655 }
656 } elseif ($action == 'set_dispute_status' && $usercancreate) { // Set dispute status
657 $result = $object->setStatut(GETPOSTINT('dispute_status'), null, 'facture', 'FACTURE_MODIFY', 'dispute_status');
658 if ($result < 0) {
659 setEventMessages($object->error, $object->errors, 'errors');
660 }
661 } elseif ($action == 'settags' && isModEnabled('category') && $usercancreate) { // Set tags
662 $result = $object->setCategories(GETPOST('categories', 'array'));
663 if ($result < 0) {
664 setEventMessages($object->error, $object->errors, 'errors');
665 }
666 } elseif ($action == 'setbankaccount' && $usercancreate) { // Bank account
667 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
668 if ($result < 0) {
669 setEventMessages($object->error, $object->errors, 'errors');
670 }
671 } elseif ($action == 'setremisepercent' && $usercancreate) {
672 $object->fetch($id);
673 $result = $object->setDiscount($user, (float) price2num(GETPOST('remise_percent'), '', 2));
674 if ($result < 0) {
675 setEventMessages($object->error, $object->errors, 'errors');
676 }
677 } elseif ($action == 'setabsolutediscount' && $usercancreate) {
678 // We have POST[remise_id] (common case) xor POST[remise_id_for_payment] (with unstable oldoption)
679 $db->begin();
680
681 // We use the credit note to reduce amount of invoice
682 if (GETPOSTINT("remise_id") > 0) {
683 $ret = $object->fetch($id);
684 if ($ret > 0) {
685 $result = $object->insert_discount(GETPOSTINT("remise_id"));
686 if ($result < 0) {
687 setEventMessages($object->error, $object->errors, 'errors');
688 }
689 } else {
690 $error++;
691 setEventMessages($object->error, $object->errors, 'errors');
692 }
693
694 if (!$error) {
695 if ($object->status == Facture::STATUS_VALIDATED) {
696 $newremaintopay = $object->getRemainToPay(0);
697 if ($newremaintopay == 0) {
698 $object->setPaid($user);
699 }
700 }
701 }
702 }
703 // We use the credit note to reduce remain to pay
704 if (GETPOSTINT("remise_id_for_payment") > 0) {
705 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
706 $discount = new DiscountAbsolute($db);
707 $discount->fetch(GETPOSTINT("remise_id_for_payment"));
708
709 //var_dump($object->getRemainToPay(0));
710 //var_dump($discount->amount_ttc);exit;
711 $remaintopay = $object->getRemainToPay(0);
712 if (price2num($discount->total_ttc) > price2num($remaintopay)) {
713 // TODO Split the discount in 2 automatically
714 $error++;
715 setEventMessages($langs->trans("ErrorDiscountLargerThanRemainToPaySplitItBefore"), null, 'errors');
716 }
717
718 if (!$error) {
719 $result = $discount->link_to_invoice(0, $id);
720 if ($result < 0) {
721 $error++;
722 setEventMessages($discount->error, $discount->errors, 'errors');
723 }
724 }
725
726 if (!$error) {
727 // Only mark as paid when the invoice is already validated. On a still-draft
728 // invoice the discount link reduces the remain_to_pay but the invoice is not
729 // yet a legally issued document (ref is still PROV-...), so closing it would
730 // leave it stuck as paid+PROV (see #37744).
731 if ($object->status == Facture::STATUS_VALIDATED) {
732 $newremaintopay = $object->getRemainToPay(0);
733 if ($newremaintopay == 0) {
734 $object->setPaid($user);
735 }
736 }
737 }
738 }
739
740 if (!$error) {
741 $db->commit();
742 } else {
743 $db->rollback();
744 }
745
746 if (empty($error) && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
747 $outputlangs = $langs;
748 $newlang = '';
749 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
750 $newlang = GETPOST('lang_id', 'aZ09');
751 }
752 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
753 $object->fetch_thirdparty();
754 $newlang = $object->thirdparty->default_lang;
755 }
756 if (!empty($newlang)) {
757 $outputlangs = new Translate("", $conf);
758 $outputlangs->setDefaultLang($newlang);
759 }
760 $ret = $object->fetch($id); // Reload to get new records
761
762 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
763 if ($result < 0) {
764 setEventMessages($object->error, $object->errors, 'errors');
765 }
766 }
767 } elseif ($action == 'setref' && $usercancreate) {
768 $object->fetch($id);
769 $object->setValueFrom('ref', GETPOST('ref'), '', 0, '', '', $user, 'BILL_MODIFY');
770 if ($result < 0) {
771 setEventMessages($object->error, $object->errors, 'errors');
772 }
773 } elseif ($action == 'setref_client' && $usercancreate) {
774 $object->fetch($id);
775 $object->set_ref_client(GETPOST('ref_client', 'alpha'));
776 if ($result < 0) {
777 setEventMessages($object->error, $object->errors, 'errors');
778 }
779 } elseif ($action == 'setdemandreason' && $usercancreate) {
780 $result = $object->setInputReason($inputReasonId);
781 if ($result < 0) {
782 setEventMessages($object->error, $object->errors, 'errors');
783 }
784 } elseif ($action == 'confirm_valid' && $confirm == 'yes' && $usercanvalidate) {
785 // Classify to validated
786 $idwarehouse = GETPOSTINT('idwarehouse');
787
788 $object->fetch($id);
789 $object->fetch_thirdparty();
790
791 // Check for warehouse
792 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
793 $qualified_for_stock_change = 0;
794 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
795 $qualified_for_stock_change = $object->hasProductsOrServices(2);
796 } else {
797 $qualified_for_stock_change = $object->hasProductsOrServices(1);
798 }
799
800 if ($qualified_for_stock_change) {
801 if (!$idwarehouse || $idwarehouse == - 1) {
802 $error++;
803 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
804 $action = '';
805 }
806 }
807 }
808
809 if (!$error) {
810 $result = $object->validate($user, '', $idwarehouse);
811 if ($result >= 0) {
812 // Define output language
813 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
814 $outputlangs = $langs;
815 $newlang = '';
816 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
817 $newlang = GETPOST('lang_id', 'aZ09');
818 }
819 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
820 $newlang = $object->thirdparty->default_lang;
821 }
822 if (!empty($newlang)) {
823 $outputlangs = new Translate("", $conf);
824 $outputlangs->setDefaultLang($newlang);
825 $outputlangs->load('products');
826 }
827 $model = $object->model_pdf;
828
829 $ret = $object->fetch($id); // Reload to get new records
830
831 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
832 if ($result < 0) {
833 setEventMessages($object->error, $object->errors, 'errors');
834 }
835 }
836 } else {
837 if (count($object->errors)) {
838 setEventMessages(null, $object->errors, 'errors');
839 } else {
840 setEventMessages($object->error, $object->errors, 'errors');
841 }
842 }
843 }
844 } elseif ($action == 'confirm_modif' && $usercanunvalidate && !getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
845 // Go back to draft status (unvalidate)
846 $idwarehouse = GETPOSTINT('idwarehouse');
847
848 $object->fetch($id);
849 $object->fetch_thirdparty();
850
851 // Check parameters
852 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
853 $qualified_for_stock_change = 0;
854 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
855 $qualified_for_stock_change = $object->hasProductsOrServices(2);
856 } else {
857 $qualified_for_stock_change = $object->hasProductsOrServices(1);
858 }
859
860 if ($qualified_for_stock_change) {
861 if (!$idwarehouse || $idwarehouse == -1) {
862 $error++;
863 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
864 $action = '';
865 }
866 }
867 }
868
869 if (!$error) {
870 // We check if invoice has payments
871 $totalpaid = 0;
872 $sql = 'SELECT pf.amount';
873 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'paiement_facture as pf';
874 $sql .= ' WHERE pf.fk_facture = ' . ((int) $object->id);
875
876 $result = $db->query($sql);
877 if ($result) {
878 $i = 0;
879 $num = $db->num_rows($result);
880
881 while ($i < $num) {
882 $objp = $db->fetch_object($result);
883 $totalpaid += $objp->amount;
884 $i++;
885 }
886 } else {
887 dol_print_error($db, '');
888 }
889
890 $resteapayer = $object->total_ttc - $totalpaid;
891
892 // We check that invoice lines are transferred into accountancy
893 $ventilExportCompta = $object->getVentilExportCompta();
894
895 // We check if no payment has been made
896 if ($ventilExportCompta == 0) {
897 if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == $object->total_ttc && empty($object->paye))) {
898 // Set invoice to draft status
899 $result = $object->setDraft($user, $idwarehouse);
900
901 if ($result < 0) {
902 setEventMessages($object->error, $object->errors, 'errors');
903 } else {
904 // Define output language
905 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
906 $outputlangs = $langs;
907 $newlang = '';
908 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
909 $newlang = GETPOST('lang_id', 'aZ09');
910 }
911 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
912 $newlang = $object->thirdparty->default_lang;
913 }
914 if (!empty($newlang)) {
915 $outputlangs = new Translate("", $conf);
916 $outputlangs->setDefaultLang($newlang);
917 $outputlangs->load('products');
918 }
919 $model = $object->model_pdf;
920 $ret = $object->fetch($id); // Reload to get new records
921
922 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
923 }
924 }
925 }
926 }
927 }
928 } elseif ($action == 'confirm_paid' && $confirm == 'yes' && $usercanissuepayment) {
929 // Classify "paid"
930 $object->fetch($id);
931 $result = $object->setPaid($user);
932 if ($result < 0) {
933 setEventMessages($object->error, $object->errors, 'errors');
934 }
935 } elseif ($action == 'confirm_paid_partially' && $confirm == 'yes' && $usercanissuepayment) {
936 // Classif "paid partially"
937 $object->fetch($id);
938 $close_code = GETPOST("close_code", 'restricthtml');
939 $close_note = GETPOST("close_note", 'restricthtml');
940 if ($close_code) {
941 // if VatRefund
942 if (isModEnabled('tax') && $close_code == $object::CLOSECODE_WITHHOLDINGTAX) {
943 require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php';
944 $resteapayer = GETPOSTFLOAT("resteapayer");
945 $amount = (float) ($resteapayer > 0 ? $resteapayer * -1 : $resteapayer);
946 if ($amount < 0) {
947 $db->begin();
948 $tempTva = new Tva($db);
949 $tempTva->datev = $object->date;
950 $tempTva->datep = $object->date;
951 $tempTva->amount = $amount;
952 $tempTva->label = $langs->trans('WithholdingTax') . ' - ' . $object->ref;
953 //$tempTva->paye = 1;
954 $valid = $tempTva->getIdForLabel($tempTva->label);
955 if (!$valid) {
956 $ret = $tempTva->create($user);
957 if ($ret < 0) {
958 $error++;
959 } else {
960 $tempTva->setPaid($user);
961 }
962 if (empty($error)) {
963 $db->commit();
964 } else {
965 setEventMessages($tempTva->error, $tempTva->errors, 'errors');
966 $db->rollback();
967 }
968 } else {
969 $error++;
970 setEventMessages($langs->trans('LabelWithholdingExist'), $tempTva->errors, 'errors');
971 $db->rollback();
972 }
973 }
974 }
975 if (!$error) {
976 $result = $object->setPaid($user, $close_code, $close_note);
977 if ($result < 0) {
978 setEventMessages($object->error, $object->errors, 'errors');
979 } else {
980 $object->fetch($object->id); // Reload properties
981 }
982 }
983 } else {
984 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
985 }
986 } elseif ($action == 'confirm_canceled' && $confirm == 'yes' && $usercancreate) {
987 // Classify "abandoned"
988 $object->fetch($id);
989 $close_code = GETPOST("close_code", 'restricthtml');
990 $close_note = GETPOST("close_note", 'restricthtml');
991 if ($close_code) {
992 $result = $object->setCanceled($user, $close_code, $close_note);
993 if ($result < 0) {
994 setEventMessages($object->error, $object->errors, 'errors');
995 } else {
996 $object->fetch($object->id); // Reload properties
997 }
998 } else {
999 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
1000 }
1001 } elseif ($action == 'confirm_converttoreduc' && $confirm == 'yes' && $usercancreate) {
1002 // Convert to discount
1003 $object->fetch($id);
1004 $object->fetch_thirdparty();
1005 //$object->fetch_lines(); // Already done into fetch
1006
1007 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1008 $discountcheck = new DiscountAbsolute($db);
1009 $result = $discountcheck->fetch(0, $object->id);
1010
1011 $canconvert = 0;
1012 if ($object->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1013 $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)
1014 }
1015 if (($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_SITUATION) && $object->paye == 0 && empty($discountcheck->id)) {
1016 $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)
1017 }
1018
1019 if ($canconvert) {
1020 $db->begin();
1021
1022 $amount_ht = $amount_tva = $amount_ttc = $amount_localtax1 = $amount_localtax2 = array();
1023 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1024
1025 // Loop on each vat rate
1026 $i = 0;
1027 foreach ($object->lines as $line) {
1028 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
1029 $keyforvatrate = $line->tva_tx.($line->vat_src_code ? ' ('.$line->vat_src_code.')' : '');
1030
1031 if (!isset($amount_ht[$keyforvatrate])) {
1032 $amount_ht[$keyforvatrate] = 0;
1033 }
1034 $amount_ht[$keyforvatrate] += $line->total_ht;
1035 if (!isset($amount_tva[$keyforvatrate])) {
1036 $amount_tva[$keyforvatrate] = 0;
1037 }
1038 $amount_tva[$keyforvatrate] += $line->total_tva;
1039 if (!isset($amount_localtax1[$keyforvatrate])) {
1040 $amount_localtax1[$keyforvatrate] = 0;
1041 }
1042 $amount_localtax1[$keyforvatrate] += $line->total_localtax1;
1043 if (!isset($amount_localtax2[$keyforvatrate])) {
1044 $amount_localtax2[$keyforvatrate] = 0;
1045 }
1046 $amount_localtax2[$keyforvatrate] += $line->total_localtax2;
1047 if (!isset($amount_ttc[$keyforvatrate])) {
1048 $amount_ttc[$keyforvatrate] = 0;
1049 }
1050 $amount_ttc[$keyforvatrate] += $line->total_ttc;
1051 if (!isset($multicurrency_amount_ht[$keyforvatrate])) {
1052 $multicurrency_amount_ht[$keyforvatrate] = 0;
1053 }
1054 $multicurrency_amount_ht[$keyforvatrate] += $line->multicurrency_total_ht;
1055 if (!isset($multicurrency_amount_tva[$keyforvatrate])) {
1056 $multicurrency_amount_tva[$keyforvatrate] = 0;
1057 }
1058 $multicurrency_amount_tva[$keyforvatrate] += $line->multicurrency_total_tva;
1059 if (!isset($multicurrency_amount_ttc[$keyforvatrate])) {
1060 $multicurrency_amount_ttc[$keyforvatrate] = 0;
1061 }
1062 $multicurrency_amount_ttc[$keyforvatrate] += $line->multicurrency_total_ttc;
1063 $i++;
1064 }
1065 }
1066 '@phan-var-force array<string,float> $amount_ht
1067 @phan-var-force array<string,float> $amount_tva
1068 @phan-var-force array<string,float> $amount_ttc
1069 @phan-var-force array<string,float> $amount_localtax1
1070 @phan-var-force array<string,float> $amount_localtax2
1071 @phan-var-force array<string,float> $multicurrency_amount_ht
1072 @phan-var-force array<string,float> $multicurrency_amount_tva
1073 @phan-var-force array<string,float> $multicurrency_amount_ttc';
1074
1075 // If some payments were already done, we change the amount to pay using same prorate
1076 if (getDolGlobalString('INVOICE_ALLOW_REUSE_OF_CREDIT_WHEN_PARTIALLY_REFUNDED') && $object->type == Facture::TYPE_CREDIT_NOTE) {
1077 $alreadypaid = $object->getSommePaiement(); // This can be not 0 if we allow to create credit to reuse from credit notes partially refunded.
1078 if ($alreadypaid && abs($alreadypaid) < abs($object->total_ttc)) {
1079 $ratio = abs(($object->total_ttc - $alreadypaid) / $object->total_ttc);
1080 foreach ($amount_ht as $vatrate => $val) {
1081 $amount_ht[$vatrate] = price2num($amount_ht[$vatrate] * $ratio, 'MU');
1082 $amount_tva[$vatrate] = price2num($amount_tva[$vatrate] * $ratio, 'MU');
1083 $amount_ttc[$vatrate] = price2num($amount_ttc[$vatrate] * $ratio, 'MU');
1084 $amount_localtax1[$vatrate] = price2num($amount_localtax1[$vatrate] * $ratio, 'MU');
1085 $amount_localtax2[$vatrate] = price2num($amount_localtax2[$vatrate] * $ratio, 'MU');
1086 $multicurrency_amount_ht[$vatrate] = price2num($multicurrency_amount_ht[$vatrate] * $ratio, 'MU');
1087 $multicurrency_amount_tva[$vatrate] = price2num($multicurrency_amount_tva[$vatrate] * $ratio, 'MU');
1088 $multicurrency_amount_ttc[$vatrate] = price2num($multicurrency_amount_ttc[$vatrate] * $ratio, 'MU');
1089 }
1090 }
1091 }
1092 //var_dump($amount_ht);var_dump($amount_tva);var_dump($amount_ttc);exit;
1093
1094 // Insert one discount by VAT rate category
1095 $discount = new DiscountAbsolute($db);
1096 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1097 $discount->description = '(CREDIT_NOTE)';
1098 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
1099 $discount->description = '(DEPOSIT)';
1101 $discount->description = '(EXCESS RECEIVED)';
1102 } else {
1103 setEventMessages($langs->trans('CantConvertToReducAnInvoiceOfThisType'), null, 'errors');
1104 }
1105 $discount->fk_soc = $object->socid;
1106 $discount->socid = $object->socid;
1107 $discount->fk_facture_source = $object->id;
1108
1109 $error = 0;
1110
1111
1112 // Create a discount that is the amount of the excess received
1114 || $object->type == Facture::TYPE_DEPOSIT) {
1115 // If we have an excess received that need to create a discount in TTC without VAT
1116 $discount->description = '(EXCESS RECEIVED)';
1117
1118 // Total payments
1119 $sql = 'SELECT SUM(pf.amount) as total_paiements';
1120 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1121 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1122 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id);
1123 $sql .= ' AND pf.fk_paiement = p.rowid';
1124 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1125 $resql = $db->query($sql);
1126 if (!$resql) {
1128 }
1129
1130 $res = $db->fetch_object($resql);
1131 $total_paiements = $res->total_paiements;
1132
1133 // Total credit note and deposit
1134 $total_creditnote_and_deposit = 0;
1135 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1136 $sql .= " re.description, re.fk_facture_source";
1137 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1138 $sql .= " WHERE fk_facture = ".((int) $object->id);
1139 $resql = $db->query($sql);
1140 if (!empty($resql)) {
1141 while ($obj = $db->fetch_object($resql)) {
1142 $total_creditnote_and_deposit += $obj->amount_ttc;
1143 }
1144 } else {
1146 }
1147
1148 $discount->amount_ttc = price2num($total_paiements + $total_creditnote_and_deposit - $object->total_ttc, 'MT');
1149 $discount->amount_tva = 0;
1150 $discount->amount_ht = $discount->amount_ttc;
1151 $discount->tva_tx = 0;
1152 $discount->localtax1_tx = 0;
1153 $discount->localtax1_type = 0;
1154 $discount->localtax2_tx = 0;
1155 $discount->localtax2_type = 0;
1156 $discount->vat_src_code = '';
1157
1158 if ($discount->amount_ttc > 0) {
1159 $result = $discount->create($user);
1160 if ($result < 0) {
1161 $error++;
1162 }
1163 }
1164 }
1165
1166 // Create a discount that is the amount of the invoice
1168 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1169 $discount->description = '(CREDIT_NOTE)';
1170 } else {
1171 $discount->description = '(DEPOSIT)';
1172 }
1173
1174 foreach ($amount_ht as $tva_tx => $xxx) {
1175 // Get localtaxes from TVA tx
1176 $taxes = getTaxesFromId($tva_tx, $object->thirdparty, $mysoc, 0);
1177
1178 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
1179 $discount->amount_ht = -((float) $amount_ht[$tva_tx]);
1180 $discount->amount_tva = -((float) $amount_tva[$tva_tx]);
1181 $discount->amount_ttc = -((float) $amount_ttc[$tva_tx]);
1182 $discount->total_ht = -((float) $amount_ht[$tva_tx]);
1183 $discount->total_tva = -((float) $amount_tva[$tva_tx]);
1184 $discount->total_localtax1 = -((float) $amount_localtax1[$tva_tx]);
1185 $discount->total_localtax2 = -((float) $amount_localtax2[$tva_tx]);
1186 $discount->total_ttc = -((float) $amount_ttc[$tva_tx]);
1187 $discount->multicurrency_amount_ht = -((float) $multicurrency_amount_ht[$tva_tx]);
1188 $discount->multicurrency_amount_tva = -((float) $multicurrency_amount_tva[$tva_tx]);
1189 $discount->multicurrency_amount_ttc = -((float) $multicurrency_amount_ttc[$tva_tx]);
1190 $discount->multicurrency_total_ht = -((float) $multicurrency_amount_ht[$tva_tx]);
1191 $discount->multicurrency_total_tva = -((float) $multicurrency_amount_tva[$tva_tx]);
1192 $discount->multicurrency_total_ttc = -((float) $multicurrency_amount_ttc[$tva_tx]);
1193 } else {
1194 //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
1195 $discount->amount_ht = abs((float) $amount_ht[$tva_tx]);
1196 $discount->amount_tva = abs((float) $amount_tva[$tva_tx]);
1197 $discount->amount_ttc = abs((float) $amount_ttc[$tva_tx]);
1198 $discount->total_ht = abs((float) $amount_ht[$tva_tx]);
1199 $discount->total_tva = abs((float) $amount_tva[$tva_tx]);
1200 $discount->total_localtax1 = abs((float) $amount_localtax1[$tva_tx]);
1201 $discount->total_localtax2 = abs((float) $amount_localtax2[$tva_tx]);
1202 $discount->total_ttc = abs((float) $amount_ttc[$tva_tx]);
1203 $discount->multicurrency_amount_ht = abs((float) $multicurrency_amount_ht[$tva_tx]);
1204 $discount->multicurrency_amount_tva = abs((float) $multicurrency_amount_tva[$tva_tx]);
1205 $discount->multicurrency_amount_ttc = abs((float) $multicurrency_amount_ttc[$tva_tx]);
1206 $discount->multicurrency_total_ht = abs((float) $multicurrency_amount_ht[$tva_tx]);
1207 $discount->multicurrency_total_tva = abs((float) $multicurrency_amount_tva[$tva_tx]);
1208 $discount->multicurrency_total_ttc = abs((float) $multicurrency_amount_ttc[$tva_tx]);
1209 }
1210
1211 // Clean vat code
1212 $reg = array();
1213 $vat_src_code = '';
1214 if (preg_match('/\‍((.*)\‍)/', $tva_tx, $reg)) {
1215 $vat_src_code = $reg[1];
1216 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx); // Remove code into vatrate.
1217 }
1218
1219 $discount->tva_tx = abs((float) $tva_tx);
1220 $discount->localtax1_tx = $taxes['localtax1'];
1221 $discount->localtax1_type = $taxes['localtax1_type'];
1222 $discount->localtax2_tx = $taxes['localtax2'];
1223 $discount->localtax2_type = $taxes['localtax2_type'];
1224 $discount->vat_src_code = $vat_src_code;
1225
1226 $result = $discount->create($user);
1227 if ($result < 0) {
1228 $error++;
1229 break;
1230 }
1231 }
1232 }
1233
1234 if (empty($error)) {
1235 // Set invoice as paid, unless it's a down payment converted to credit without any payment received
1236 // (option DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID allows creating the discount/credit even if the down payment
1237 // has not been paid yet; in that case we must NOT mark it as paid since no payment was actually received)
1238 $skipSetPaid = ($object->type == Facture::TYPE_DEPOSIT && getDolGlobalInt('DEPOSIT_CAN_BE_CONVERTED_AS_AVAILABLE_CREDIT_EVEN_IF_NOT_YET_PAID') && price2num($object->getSommePaiement(), 'MT') == 0);
1239
1240 if ($skipSetPaid) {
1241 $object->fetch($object->id); // Reload properties
1242 $db->commit();
1243 } else {
1244 $result = $object->setPaid($user); // We can close the invoice. Even if we got an excess received, it is now into discounts.
1245 if ($result >= 0) {
1246 $object->fetch($object->id); // Reload properties
1247 $db->commit();
1248 } else {
1249 setEventMessages($object->error, $object->errors, 'errors');
1250 $db->rollback();
1251 }
1252 }
1253 } else {
1254 setEventMessages($discount->error, $discount->errors, 'errors');
1255 $db->rollback();
1256 }
1257 }
1258 } elseif ($action == 'confirm_delete_paiement' && $confirm == 'yes' && $usercanissuepayment) {
1259 // Delete payment
1260 $object->fetch($id);
1261 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0) {
1262 $paiement = new Paiement($db);
1263 $result = $paiement->fetch(GETPOSTINT('paiement_id'));
1264 if ($result > 0) {
1265 $result = $paiement->delete($user); // If fetch ok and found
1266 if ($result >= 0) {
1267 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
1268 exit;
1269 }
1270 }
1271 if ($result < 0) {
1272 setEventMessages($paiement->error, $paiement->errors, 'errors');
1273 }
1274 }
1275 } elseif ($action == 'add' && $usercancreate) {
1276 // Insert new invoice in database
1277 if ($socid > 0) {
1278 $object->socid = GETPOSTINT('socid');
1279 }
1280
1281 if (GETPOST('type') === '') {
1282 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1283 }
1284
1285 $db->begin();
1286
1287 $originentity = GETPOSTINT('originentity');
1288 $object->demand_reason_id = $inputReasonId;
1289
1290 // Fill array 'array_options' with data from add form
1291 $ret = $extrafields->setOptionalsFromPost(null, $object);
1292 if ($ret < 0) {
1293 $error++;
1294 }
1295
1296 $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
1297 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
1298
1299 $classname = null;
1300
1301
1302 // Add a Replacement invoice
1303 if (GETPOST('type') == Facture::TYPE_REPLACEMENT) {
1304 if (empty($dateinvoice)) {
1305 $error++;
1306 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1307 $action = 'create';
1308 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1309 $error++;
1310 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1311 $action = 'create';
1312 }
1313
1314 if (!(GETPOSTINT('fac_replacement') > 0)) {
1315 $error++;
1316 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ReplaceInvoice")), null, 'errors');
1317 $action = 'create';
1318 }
1319
1320 if (!$error) {
1321 // This is a replacement invoice
1322 $result = $object->fetch(GETPOSTINT('fac_replacement'));
1323 $object->fetch_thirdparty();
1324
1325 $object->date = $dateinvoice;
1326 $object->date_pointoftax = $date_pointoftax;
1327 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1328 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1329 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1330 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1331 $object->model_pdf = GETPOST('model', 'alphanohtml');
1332 $object->fk_project = GETPOSTINT('projectid');
1333 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
1334 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1335 $object->fk_account = GETPOSTINT('fk_account');
1336 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1337 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1338 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1339 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1340
1341 // Special properties of replacement invoice
1342 $object->fk_facture_source = GETPOSTINT('fac_replacement');
1344
1345 $id = $object->createFromCurrent($user);
1346 if ($id <= 0) {
1347 setEventMessages($object->error, $object->errors, 'errors');
1348 }
1349 }
1350 }
1351
1352 // Add a Credit note invoice
1353 if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) {
1354 $sourceinvoice = GETPOSTINT('fac_avoir');
1355 if (!($sourceinvoice > 0) && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
1356 $error++;
1357 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceAvoirAskCombo")), null, 'errors');
1358 $action = 'create';
1359 }
1360
1361 if (empty($dateinvoice)) {
1362 $error++;
1363 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1364 $action = 'create';
1365 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1366 $error++;
1367 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1368 $action = 'create';
1369 }
1370
1371 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1372 $error++;
1373 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1374 $action = 'create';
1375 }
1376
1377 if (!$error) {
1378 if (!empty($originentity)) {
1379 $object->entity = $originentity;
1380 }
1381 $object->socid = GETPOSTINT('socid');
1382 $object->subtype = GETPOSTINT('subtype');
1383 $object->ref = GETPOST('ref');
1384 $object->date = $dateinvoice;
1385 $object->date_pointoftax = $date_pointoftax;
1386 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1387 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1388 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1389 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1390 $object->model_pdf = GETPOST('model');
1391 $object->fk_project = GETPOSTINT('projectid');
1392 $object->cond_reglement_id = 0; // No payment term for a credit note
1393 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1394 $object->fk_account = GETPOSTINT('fk_account');
1395 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1396 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1397 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1398 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1399
1400 // Special properties of replacement invoice
1401 $object->fk_facture_source = $sourceinvoice > 0 ? $sourceinvoice : '';
1403
1404 $facture_source = new Facture($db); // fetch origin object
1405 if ($object->fk_facture_source > 0 && $facture_source->fetch($object->fk_facture_source) > 0) {
1406 if ($facture_source->isSituationInvoice()) {
1407 $object->situation_counter = $facture_source->situation_counter;
1408 $object->situation_cycle_ref = $facture_source->situation_cycle_ref;
1409 $facture_source->fetchPreviousNextSituationInvoice();
1410 }
1411
1412 $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
1413 $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
1414 }
1415
1416 $id = $object->create($user);
1417 if ($id < 0) {
1418 $error++;
1419 } else {
1420 // copy internal contacts
1421 if ($object->copy_linked_contact($facture_source, 'internal') < 0) {
1422 $error++;
1423 } elseif ($facture_source->socid == $object->socid) {
1424 // copy external contacts if same company
1425 if ($object->copy_linked_contact($facture_source, 'external') < 0) {
1426 $error++;
1427 }
1428 }
1429 }
1430
1431 // NOTE: Pb with situation invoice when INVOICE_USE_SITUATION=1 (legacy mode)
1432 // NOTE: fields total on situation invoice are stored as cumulative values on total of lines (bad) but delta on invoice total
1433 // NOTE: fields total on credit note are stored as delta both on total of lines and on invoice total (good)
1434 // NOTE: fields situation_percent on situation invoice are stored as cumulative values on lines (bad)
1435 // NOTE: fields situation_percent on credit note are stored as delta on lines (good)
1436 if (GETPOSTINT('invoiceAvoirWithLines') == 1 && $id > 0) {
1437 if (!empty($facture_source->lines)) {
1438 $fk_parent_line = 0;
1439
1440 foreach ($facture_source->lines as $line) {
1441 // Extrafields
1442 if (method_exists($line, 'fetch_optionals')) {
1443 // load extrafields
1444 $line->fetch_optionals();
1445 }
1446
1447 // Reset fk_parent_line for no child products and special product
1448 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1449 $fk_parent_line = 0;
1450 }
1451
1452
1453 if ($facture_source->isSituationInvoice()) {
1454 $source_fk_prev_id = $line->fk_prev_id; // temporary storing situation invoice fk_prev_id
1455 $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
1456
1457 // The subtraction below assumes total_ht/situation_percent are stored cumulative on situation invoice lines
1458 // (INVOICE_USE_SITUATION = 1, legacy). In progressive mode (INVOICE_USE_SITUATION = 2), each line already
1459 // holds its own delta, so subtracting the previous invoice's line here would credit the wrong amount.
1460 if (getDolGlobalInt('INVOICE_USE_SITUATION') != 2 && !empty($facture_source->tab_previous_situation_invoice)) {
1461 // search the last standard invoice in cycle and the possible credit note between this last and facture_source
1462 // TODO Move this out of loop of $facture_source->lines
1463 $tab_jumped_credit_notes = array();
1464 $lineIndex = count($facture_source->tab_previous_situation_invoice) - 1;
1465 $searchPreviousInvoice = true;
1466 while ($searchPreviousInvoice) {
1467 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_SITUATION || $lineIndex < 1) {
1468 $searchPreviousInvoice = false; // find, exit;
1469 break;
1470 } else {
1471 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_CREDIT_NOTE) {
1472 $tab_jumped_credit_notes[$lineIndex] = $facture_source->tab_previous_situation_invoice[$lineIndex]->id;
1473 }
1474 $lineIndex--; // go to previous invoice in cycle
1475 }
1476 }
1477
1478 $maxPrevSituationPercent = 0;
1479 foreach ($facture_source->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
1480 if ($prevLine->id == $source_fk_prev_id) {
1481 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
1482
1483 //$line->subprice = $line->subprice - $prevLine->subprice;
1484 $line->total_ht -= $prevLine->total_ht;
1485 $line->total_tva -= $prevLine->total_tva;
1486 $line->total_ttc -= $prevLine->total_ttc;
1487 $line->total_localtax1 -= $prevLine->total_localtax1;
1488 $line->total_localtax2 -= $prevLine->total_localtax2;
1489
1490 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1491 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1492 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1493 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1494 }
1495 }
1496
1497 // prorata
1498 $line->situation_percent = $maxPrevSituationPercent - $line->situation_percent;
1499
1500 //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>';
1501
1502 // If there is some credit note between last situation invoice and invoice used for credit note generation (note: credit notes are stored as delta)
1503 $maxPrevSituationPercent = 0;
1504 foreach ($tab_jumped_credit_notes as $index => $creditnoteid) {
1505 foreach ($facture_source->tab_previous_situation_invoice[$index]->lines as $prevLine) {
1506 if ($prevLine->fk_prev_id == $source_fk_prev_id) {
1507 $maxPrevSituationPercent = $prevLine->situation_percent;
1508
1509 $line->total_ht -= $prevLine->total_ht;
1510 $line->total_tva -= $prevLine->total_tva;
1511 $line->total_ttc -= $prevLine->total_ttc;
1512 $line->total_localtax1 -= $prevLine->total_localtax1;
1513 $line->total_localtax2 -= $prevLine->total_localtax2;
1514
1515 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1516 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1517 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1518 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1519 }
1520 }
1521 }
1522
1523 // prorata
1524 $line->situation_percent += $maxPrevSituationPercent;
1525
1526 //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>';
1527 }
1528 }
1529
1530 $line->fk_facture = $object->id;
1531 $line->fk_parent_line = $fk_parent_line;
1532
1533 $line->subprice = -$line->subprice; // invert price for object
1534 // $line->pa_ht = $line->pa_ht; // we chose to have buy/cost price always positive, so no revert of sign here
1535 $line->total_ht = -$line->total_ht;
1536 $line->total_tva = -$line->total_tva;
1537 $line->total_ttc = -$line->total_ttc;
1538 $line->total_localtax1 = -$line->total_localtax1;
1539 $line->total_localtax2 = -$line->total_localtax2;
1540
1541 $line->multicurrency_subprice = -$line->multicurrency_subprice;
1542 $line->multicurrency_total_ht = -$line->multicurrency_total_ht;
1543 $line->multicurrency_total_tva = -$line->multicurrency_total_tva;
1544 $line->multicurrency_total_ttc = -$line->multicurrency_total_ttc;
1545
1546 $line->context['createcreditnotefrominvoice'] = 1;
1547 $result = $line->insert(0, 1); // When creating credit note with same lines than source, we must ignore error if discount already linked
1548
1549 $object->lines[] = $line; // insert new line in current object
1550
1551 // Defined the new fk_parent_line
1552 if ($result > 0 && $line->product_type == 9) {
1553 $fk_parent_line = $result;
1554 }
1555 }
1556
1557 $object->update_price(1);
1558 }
1559 }
1560
1561 if (GETPOSTINT('invoiceAvoirWithPaymentRestAmount') == 1 && $id > 0) {
1562 if ($facture_source->fetch($object->fk_facture_source) > 0) {
1563 $totalpaid = $facture_source->getSommePaiement();
1564 $totalcreditnotes = $facture_source->getSumCreditNotesUsed();
1565 $totaldeposits = $facture_source->getSumDepositsUsed();
1566 $remain_to_pay = abs($facture_source->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits);
1567
1568 if (getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') == 'default') {
1569 if ((empty($object->thirdparty) || !is_object($object->thirdparty) || get_class($object->thirdparty) != 'Societe')) {
1570 $object->fetch_thirdparty();
1571 }
1572 if (!empty($object->thirdparty) && is_object($object->thirdparty) && get_class($object->thirdparty) == 'Societe') {
1573 $tva_tx = get_default_tva($mysoc, $object->thirdparty);
1574 } else {
1575 $tva_tx = 0;
1576 }
1577 } elseif ((float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') > 0) {
1578 $tva_tx = (float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY');
1579 } else {
1580 $tva_tx = 0;
1581 }
1582
1583 $object->addline($langs->trans('invoiceAvoirLineWithPaymentRestAmount'), 0, 1, $tva_tx, 0, 0, 0, 0, '', '', 0, 0, 0, 'TTC', $remain_to_pay);
1584 }
1585 }
1586
1587 // Add link between credit note and origin
1588 if (!empty($object->fk_facture_source) && $id > 0) {
1589 $facture_source->fetch($object->fk_facture_source);
1590 $facture_source->fetchObjectLinked();
1591
1592 if (!empty($facture_source->linkedObjectsIds)) {
1593 foreach ($facture_source->linkedObjectsIds as $sourcetype => $TIds) {
1594 $object->add_object_linked($sourcetype, current($TIds));
1595 }
1596 }
1597 }
1598 }
1599 }
1600
1601 // Standard invoice or Deposit invoice, created from a Predefined template invoice
1602 if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT) && GETPOSTINT('fac_rec') > 0) {
1603 if (empty($dateinvoice)) {
1604 $error++;
1605 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1606 $action = 'create';
1607 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1608 $error++;
1609 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1610 $action = 'create';
1611 }
1612
1613
1614 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1615 $error++;
1616 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1617 $action = 'create';
1618 }
1619
1620 if (!$error) {
1621 $object->socid = GETPOSTINT('socid');
1622 $object->type = GETPOSTINT('type');
1623 $object->subtype = GETPOSTINT('subtype');
1624 $object->ref = GETPOST('ref');
1625 $object->date = $dateinvoice;
1626 $object->date_pointoftax = $date_pointoftax;
1627 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1628 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1629
1630 $object->ref_customer = GETPOST('ref_client');
1631
1632 $object->model_pdf = GETPOST('model');
1633 $object->fk_project = GETPOSTINT('projectid');
1634 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1635 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1636 $object->fk_account = GETPOSTINT('fk_account');
1637 $object->amount = price2num(GETPOST('amount'));
1638 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1639 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1640 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1641 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1642
1643 // Source facture
1644 $object->fac_rec = GETPOSTINT('fac_rec');
1645
1646 $id = $object->create($user); // This include recopy of links from recurring invoice and recurring invoice lines
1647 }
1648 }
1649
1650 // Standard or deposit invoice, not from a Predefined template invoice
1651 if ((GETPOST('type') == Facture::TYPE_STANDARD
1652 || GETPOST('type') == Facture::TYPE_DEPOSIT
1653 || GETPOST('type') == Facture::TYPE_PROFORMA
1654 || (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') <= 0))
1655 && GETPOST('fac_rec') <= 0) {
1656 $typeamount = GETPOST('typedeposit', 'aZ09');
1657 $valuestandardinvoice = price2num(str_replace('%', '', GETPOST('valuestandardinvoice', 'alpha')), 'MU');
1658 $valuedeposit = price2num(str_replace('%', '', GETPOST('valuedeposit', 'alpha')), 'MU');
1659
1660 if (GETPOSTINT('socid') < 1) {
1661 $error++;
1662 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Customer")), null, 'errors');
1663 $action = 'create';
1664 }
1665
1666 if (empty($dateinvoice)) {
1667 $error++;
1668 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1669 $action = 'create';
1670 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1671 $error++;
1672 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1673 $action = 'create';
1674 }
1675
1676
1677 if (GETPOST('type') == Facture::TYPE_STANDARD) {
1678 if ($valuestandardinvoice < 0 || $valuestandardinvoice > 100) {
1679 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1680 $error++;
1681 $action = 'create';
1682 }
1683 } elseif (GETPOST('type') == Facture::TYPE_DEPOSIT) {
1684 if ($typeamount && !empty($origin) && !empty($originid)) {
1685 if ($typeamount == 'amount' && $valuedeposit <= 0) {
1686 setEventMessages($langs->trans("ErrorAnAmountWithoutTaxIsRequired"), null, 'errors');
1687 $error++;
1688 $action = 'create';
1689 }
1690 if ($typeamount == 'variable' && $valuedeposit <= 0) {
1691 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1692 $error++;
1693 $action = 'create';
1694 }
1695 if ($typeamount == 'variablealllines' && $valuedeposit <= 0) {
1696 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1697 $error++;
1698 $action = 'create';
1699 }
1700 }
1701 }
1702
1703
1704 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1705 $error++;
1706 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1707 $action = 'create';
1708 }
1709
1710 if (!$error) {
1711 $object->socid = GETPOSTINT('socid');
1712 $object->type = GETPOSTINT('type');
1713 $object->subtype = GETPOSTINT('subtype');
1714 $object->ref = GETPOST('ref');
1715 $object->date = $dateinvoice;
1716 $object->date_pointoftax = $date_pointoftax;
1717 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1718 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1719 $object->ref_client = GETPOST('ref_client');
1720 $object->ref_customer = GETPOST('ref_client');
1721 $object->model_pdf = GETPOST('model');
1722 $object->fk_project = GETPOSTINT('projectid');
1723 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1724 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1725 $object->fk_account = GETPOSTINT('fk_account');
1726 $object->amount = price2num(GETPOST('amount'));
1727 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1728 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1729 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1730 $object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
1731
1732 if (GETPOST('type') == Facture::TYPE_SITUATION) {
1733 $object->situation_counter = 1;
1734 $object->situation_final = 0;
1735 $object->situation_cycle_ref = $object->newCycle();
1736 }
1737
1738 if (in_array($object->type, $retainedWarrantyInvoiceAvailableType)) {
1739 $object->retained_warranty = GETPOSTINT('retained_warranty');
1740 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
1741 } else {
1742 $object->retained_warranty = 0;
1743 $object->retained_warranty_fk_cond_reglement = 0;
1744 }
1745
1746 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
1747 if (!empty($retained_warranty_date_limit) && dol_stringtotime($retained_warranty_date_limit)) {
1748 $object->retained_warranty_date_limit = dol_stringtotime($retained_warranty_date_limit);
1749 }
1750 $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);
1751
1752 $object->fetch_thirdparty();
1753
1754 // If creation from another object of another module (Example: origin=propal, originid=1)
1755 if (!empty($origin) && !empty($originid)) {
1756 $regs = array();
1757 // Parse element/subelement (ex: project_task)
1758 $element = $subelement = $origin;
1759 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
1760 $element = $regs[1];
1761 $subelement = $regs[2];
1762 }
1763
1764 // For compatibility
1765 if ($element == 'order') {
1766 $element = $subelement = 'commande';
1767 }
1768 if ($element == 'propal') {
1769 $element = 'comm/propal';
1770 $subelement = 'propal';
1771 }
1772 if ($element == 'contract') {
1773 $element = $subelement = 'contrat';
1774 }
1775 if ($element == 'inter') {
1776 $element = $subelement = 'fichinter';
1777 }
1778 if ($element == 'shipping') {
1779 $element = $subelement = 'expedition';
1780 }
1781
1782 $object->origin = $origin; // deprecated
1783 $object->origin_type = $origin;
1784 $object->origin_id = $originid;
1785
1786 // Possibility to add external linked objects with hooks
1787 $object->linked_objects[$object->origin_type] = $object->origin_id;
1788 // link with order if it is a shipping invoice
1789 if ($object->origin == 'shipping') {
1790 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1791 $exp = new Expedition($db);
1792 $exp->fetch($object->origin_id);
1793 $exp->fetchObjectLinked();
1794 if (is_array($exp->linkedObjectsIds['commande']) && count($exp->linkedObjectsIds['commande']) > 0) {
1795 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1796 $object->linked_objects['commande'] = $value;
1797 }
1798 }
1799 }
1800
1801 if (GETPOSTISARRAY('other_linked_objects')) {
1802 $object->linked_objects = array_merge($object->linked_objects, GETPOST('other_linked_objects', 'array:int'));
1803 }
1804
1805 $id = $object->create($user); // This include class to add_object_linked() and add add_contact()
1806
1807 if ($id > 0) {
1808 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
1809
1810 $classname = ucfirst($subelement);
1811 $srcobject = new $classname($db);
1812 '@phan-var-force CommonObject $srcobject';
1813
1814 dol_syslog("Try to find source object origin_type=".$object->origin_type." origin_id=".$object->origin_id." to add lines or deposit lines");
1815 $result = $srcobject->fetch($object->origin_id);
1816
1817 $i = -1; // Ensure initialised for static analysis, but with invalid idx.
1818 // If deposit invoice - down payment with 1 line (fixed amount or percent)
1819 if (GETPOST('type') == Facture::TYPE_DEPOSIT && in_array($typeamount, array('amount', 'variable'))) {
1820 // Define the array $amountdeposit
1821 $amountdeposit = array();
1822 $lines = array();
1823 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA')) { // We want to split the discount line into several lines, one per vat rate.
1824 if ($typeamount == 'amount') {
1825 $amount = (float) $valuedeposit;
1826 } else {
1827 $amount = $srcobject->total_ttc * ((float) $valuedeposit / 100);
1828 }
1829
1830 $TTotalByTva = array();
1831 foreach ($srcobject->lines as &$line) {
1832 if (empty($line->qty)) {
1833 continue; // We discard qty=0, it is an option
1834 }
1835 if (!empty($line->special_code)) {
1836 continue;
1837 }
1838 $TTotalByTva[$line->tva_tx] += $line->total_ttc;
1839 }
1840 '@phan-var-force array<string,float> $TTotalByTva';
1841
1842 $amount_ttc_diff = 0.;
1843 foreach ($TTotalByTva as $tva => &$total) {
1844 if (empty($amountdeposit[$tva])) {
1845 $amountdeposit[$tva] = 0;
1846 }
1847 $coef = $total / $srcobject->total_ttc; // Calc coef
1848 $am = $amount * $coef;
1849 $amount_ttc_diff += $am;
1850 $amountdeposit[$tva] += $am / (1 + (float) $tva / 100); // Convert into HT for the addline
1851 }
1852 } else {
1853 if ($typeamount == 'amount') {
1854 $amountdeposit[0] = $valuedeposit;
1855 } elseif ($typeamount == 'variable') {
1856 if ($result > 0) {
1857 $totalamount = 0;
1858 $lines = $srcobject->lines;
1859 $numlines = count($lines);
1860 for ($i = 0; $i < $numlines; $i++) {
1861 $qualified = 1;
1862 if (empty($lines[$i]->qty)) {
1863 $qualified = 0; // We discard qty=0, it is an option
1864 }
1865 if (!empty($lines[$i]->special_code)) {
1866 $qualified = 0; // We discard special_code (frais port, ecotaxe, option, ...)
1867 }
1868 if ($qualified) {
1869 $totalamount += $lines[$i]->total_ht; // Fixme : is it not for the customer ? Shouldn't we take total_ttc ?
1870 $tva_tx = $lines[$i]->tva_tx;
1871
1872 if (empty($amountdeposit[$tva_tx])) {
1873 $amountdeposit[$tva_tx] = 0;
1874 }
1875 $amountdeposit[$tva_tx] += ($lines[$i]->total_ht * (float) $valuedeposit) / 100;
1876 }
1877 }
1878
1879 if ($totalamount == 0) {
1880 $amountdeposit[0] = 0;
1881 }
1882 } else {
1883 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
1884 $error++;
1885 }
1886 }
1887
1888 $amount_ttc_diff = $amountdeposit[0];
1889 }
1890
1891 foreach ($amountdeposit as $tva => $amount) {
1892 if (empty($amount)) {
1893 continue;
1894 }
1895
1896 $arraylist = array(
1897 'amount' => 'FixAmount',
1898 'variable' => 'VarAmount'
1899 );
1900 $descline = '(DEPOSIT)';
1901 //$descline.= ' - '.$langs->trans($arraylist[$typeamount]);
1902 if ($typeamount == 'amount') {
1903 $descline .= ' ('.price($valuedeposit, 0, $langs, 0, - 1, - 1, (!empty($object->multicurrency_code) ? $object->multicurrency_code : $conf->currency)).')';
1904 } elseif ($typeamount == 'variable') {
1905 $descline .= ' ('.$valuedeposit.'%)';
1906 }
1907
1908 $descline .= ' - '.$srcobject->ref;
1909 $result = $object->addline(
1910 $descline,
1911 (float) $amount, // subprice
1912 1, // quantity
1913 $tva, // vat rate
1914 0, // localtax1_tx
1915 0, // localtax2_tx
1916 getDolGlobalInt('INVOICE_PRODUCTID_DEPOSIT'), // fk_product
1917 0, // remise_percent
1918 0, // date_start
1919 0, // date_end
1920 0,
1921 $i >= 0 ? $lines[$i]->info_bits : 0, // info_bits
1922 0,
1923 'HT',
1924 0,
1925 0, // product_type
1926 1,
1927 $i >= 0 ? $lines[$i]->special_code : 0,
1928 $object->origin_type,
1929 0,
1930 0,
1931 0,
1932 0,
1933 '',
1934 array(), // array_options
1935 100,
1936 0,
1937 null,
1938 0,
1939 '',
1940 (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA') ? 0 : 1)
1941 );
1942 }
1943
1944 $diff = $object->total_ttc - $amount_ttc_diff;
1945
1946 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA') && $diff != 0) {
1947 $object->fetch_lines();
1948 $subprice_diff = $object->lines[0]->subprice - $diff / (1 + $object->lines[0]->tva_tx / 100);
1949 $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);
1950 }
1951 }
1952
1953 // standard invoice, credit note, or down payment from a percent of all lines
1954 if (GETPOST('type') != Facture::TYPE_DEPOSIT || (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines')) {
1955 $lines = array();
1956
1957 if ($result > 0) {
1958 $lines = $srcobject->lines;
1959 if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
1960 $srcobject->fetch_lines();
1961 $lines = $srcobject->lines;
1962 }
1963
1964 // If we create a standard invoice with a percent, we change amount by changing the qty
1965 if (GETPOST('type') == Facture::TYPE_STANDARD && $valuestandardinvoice > 0 && $valuestandardinvoice < 100) {
1966 if (is_array($lines)) {
1967 foreach ($lines as $line) {
1968 // We keep ->subprice and ->pa_ht, but we change the qty
1969 $line->qty = (float) price2num((float) $line->qty * (float) $valuestandardinvoice / 100, 'MS');
1970 }
1971 }
1972 }
1973 // If we create a down payment with a percent on all lines, we change amount by changing the qty
1974 if (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines') {
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) $valuedeposit / 100, 'MS');
1979 }
1980 }
1981 }
1982
1983 $fk_parent_line = 0;
1984 $num = count($lines);
1985
1986 for ($i = 0; $i < $num; $i++) {
1987 if (!in_array($lines[$i]->id, $selectedLines)) {
1988 continue; // Skip unselected lines
1989 }
1990
1991 // Don't add lines with qty 0 when coming from a shipment including all order lines
1992 if ($srcobject->element == 'shipping' && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS') && $lines[$i]->qty == 0) {
1993 continue;
1994 }
1995 // Don't add closed lines when coming from a contract (Set constant to '0,5' to exclude also inactive lines)
1996 if (!isset($conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE)) {
1997 $conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE = '5';
1998 }
1999 if ($srcobject->element == 'contrat' && in_array($lines[$i]->statut, explode(',', getDolGlobalString('CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE')))) {
2000 continue;
2001 }
2002
2003 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
2004 $desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : '');
2005
2006 if ($object->situation_counter == 1) {
2007 $lines[$i]->situation_percent = 0;
2008 }
2009
2010 if ($lines[$i]->subprice < 0 && !getDolGlobalString('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN')) {
2011 // Negative line, we create a discount line
2012 if (empty($desc)) {
2013 $desc = $label ? $label : $langs->trans('Discount');
2014 }
2015
2016 $discount = new DiscountAbsolute($db);
2017 $discount->fk_soc = $object->socid;
2018 $discount->socid = $object->socid;
2019 $discount->amount_ht = abs($lines[$i]->total_ht);
2020 $discount->amount_tva = abs($lines[$i]->total_tva);
2021 $discount->amount_ttc = abs($lines[$i]->total_ttc);
2022 $discount->total_ht = abs($lines[$i]->total_ht);
2023 $discount->total_tva = abs($lines[$i]->total_tva);
2024 $discount->total_ttc = abs($lines[$i]->total_ttc);
2025 $discount->tva_tx = $lines[$i]->tva_tx;
2026 $discount->localtax1_tx = $lines[$i]->localtax1_tx;
2027 $discount->localtax2_tx = $lines[$i]->localtax2_tx;
2028 $discount->localtax1_type = $lines[$i]->localtax1_type;
2029 $discount->localtax2_type = $lines[$i]->localtax2_type;
2030 $discount->total_localtax1 = abs($lines[$i]->total_localtax1);
2031 $discount->total_localtax2 = abs($lines[$i]->total_localtax2);
2032 $discount->fk_user = $user->id;
2033 $discount->description = $desc;
2034 $discount->multicurrency_subprice = abs($lines[$i]->multicurrency_subprice);
2035 $discount->multicurrency_amount_ht = abs($lines[$i]->multicurrency_total_ht);
2036 $discount->multicurrency_amount_tva = abs($lines[$i]->multicurrency_total_tva);
2037 $discount->multicurrency_amount_ttc = abs($lines[$i]->multicurrency_total_ttc);
2038 $discount->multicurrency_total_ht = abs($lines[$i]->multicurrency_total_ht);
2039 $discount->multicurrency_total_tva = abs($lines[$i]->multicurrency_total_tva);
2040 $discount->multicurrency_total_ttc = abs($lines[$i]->multicurrency_total_ttc);
2041
2042 $discountid = $discount->create($user);
2043 if ($discountid > 0) {
2044 $result = $object->insert_discount($discountid); // This include link_to_invoice
2045 } else {
2046 setEventMessages($discount->error, $discount->errors, 'errors');
2047 $error++;
2048 break;
2049 }
2050 } else {
2051 // Positive line
2052 // we keep first type from product if exist, otherwise we keep type from line (free line) and at last default Product
2053 $product_type = $lines[$i]->product_type ?? ($lines[$i]->type ?? Product::TYPE_PRODUCT);
2054
2055 // Date start
2056 $date_start = false;
2057 if (isset($lines[$i]->date_debut_prevue)) {
2058 $date_start = $lines[$i]->date_debut_prevue;
2059 }
2060 if (isset($lines[$i]->date_debut_reel)) {
2061 $date_start = $lines[$i]->date_debut_reel;
2062 }
2063 if (isset($lines[$i]->date_start)) {
2064 $date_start = $lines[$i]->date_start;
2065 }
2066
2067 // Date end
2068 $date_end = false;
2069 if (isset($lines[$i]->date_fin_prevue)) {
2070 $date_end = $lines[$i]->date_fin_prevue;
2071 }
2072 if (isset($lines[$i]->date_fin_reel)) {
2073 $date_end = $lines[$i]->date_fin_reel;
2074 }
2075 if (isset($lines[$i]->date_end)) {
2076 $date_end = $lines[$i]->date_end;
2077 }
2078
2079 // Reset fk_parent_line for no child products and special product
2080 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
2081 $fk_parent_line = 0;
2082 }
2083
2084 $array_options = array();
2085 // Extrafields
2086 if (method_exists($lines[$i], 'fetch_optionals')) {
2087 $lines[$i]->fetch_optionals();
2088 $array_options = $lines[$i]->array_options;
2089 }
2090
2091 $tva_tx = $lines[$i]->tva_tx;
2092 if (!empty($lines[$i]->vat_src_code) && !preg_match('/\‍(/', (string) $tva_tx)) {
2093 $tva_tx .= ' ('.$lines[$i]->vat_src_code.')';
2094 }
2095
2096 // View third's localtaxes for NOW and do not use value from origin.
2097 // TODO Is this really what we want ? Yes if source is template invoice but what if proposal or order ?
2098 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty);
2099 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty);
2100
2101 $result = $object->addline(
2102 $desc,
2103 $lines[$i]->subprice,
2104 $lines[$i]->qty,
2105 $tva_tx,
2106 $localtax1_tx,
2107 $localtax2_tx,
2108 $lines[$i]->fk_product,
2109 $lines[$i]->remise_percent,
2111 $date_end,
2112 0,
2113 (int) $lines[$i]->info_bits,
2114 isset($lines[$i]->fk_remise_except) ? $lines[$i]->fk_remise_except : null,
2115 'HT',
2116 0,
2117 $product_type,
2118 $lines[$i]->rang,
2119 $lines[$i]->special_code,
2120 $object->origin_type,
2121 $lines[$i]->rowid,
2122 $fk_parent_line,
2123 isset($lines[$i]->fk_fournprice) ? $lines[$i]->fk_fournprice : null,
2124 $lines[$i]->pa_ht,
2125 $label,
2126 $array_options,
2127 $lines[$i]->situation_percent ?? 100,
2128 $lines[$i]->fk_prev_id ?? 0,
2129 $lines[$i]->fk_unit,
2130 0,
2131 '',
2132 0
2133 );
2134
2135 if ($result > 0) {
2136 foreach ($object->lines as $line) {
2137 if ($line->id == $result) {
2138 $line->extraparams = $lines[$i]->extraparams;
2139 $line->setExtraParameters();
2140 }
2141 }
2142
2143 $lineid = $result;
2144 } else {
2145 $lineid = 0;
2146 $error++;
2147 break;
2148 }
2149
2150 // Defined the new fk_parent_line
2151 if ($result > 0 && $lines[$i]->product_type == 9) {
2152 $fk_parent_line = $result;
2153 }
2154 }
2155 }
2156 } else {
2157 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
2158 $error++;
2159 }
2160 }
2161
2162 $object->update_price(1, 'auto', 0, $mysoc);
2163
2164 $object->line_order(true, 'DESC');
2165
2166 // Now we create same links to contact than the ones found on origin object
2167 /* Useless, already into the create
2168 if (getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN')) {
2169 $originforcontact = $object->origin;
2170 $originidforcontact = $object->origin_id;
2171 if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
2172 {
2173 $originforcontact=$srcobject->origin;
2174 $originidforcontact=$srcobject->origin_id;
2175 }
2176 $sqlcontact = "SELECT code, fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
2177 $sqlcontact.= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$db->escape($originforcontact)."'";
2178
2179 $resqlcontact = $db->query($sqlcontact);
2180 if ($resqlcontact)
2181 {
2182 while($objcontact = $db->fetch_object($resqlcontact))
2183 {
2184 //print $objcontact->code.'-'.$objcontact->fk_socpeople."\n";
2185 $object->add_contact($objcontact->fk_socpeople, $objcontact->code);
2186 }
2187 }
2188 else dol_print_error($resqlcontact);
2189 }*/
2190
2191 // Hooks
2192 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id, 'objFrom' => $srcobject);
2193 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
2194 // modified by hook
2195 if ($reshook < 0) {
2196 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2197 $error++;
2198 }
2199 } else {
2200 setEventMessages($object->error, $object->errors, 'errors');
2201 $error++;
2202 }
2203 } else { // If some invoice's lines coming from page
2204 $id = $object->create($user);
2205
2206 for ($i = 1; $i <= $NBLINES; $i++) {
2207 if (GETPOSTINT('idprod'.$i)) {
2208 $product = new Product($db);
2209 $product->fetch(GETPOSTINT('idprod'.$i));
2210 $startday = dol_mktime(12, 0, 0, GETPOSTINT('date_start'.$i.'month'), GETPOSTINT('date_start'.$i.'day'), GETPOSTINT('date_start'.$i.'year'));
2211 $endday = dol_mktime(12, 0, 0, GETPOSTINT('date_end'.$i.'month'), GETPOSTINT('date_end'.$i.'day'), GETPOSTINT('date_end'.$i.'year'));
2212 $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);
2213 }
2214 }
2215
2216 $object->update_price(1, 'auto', 0, $mysoc);
2217 }
2218 }
2219 }
2220
2221 // Situation invoices
2222 if (GETPOST('type') == Facture::TYPE_SITUATION && GETPOSTINT('situations') > 0) {
2223 if (empty($dateinvoice)) {
2224 $error++;
2225 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date"));
2226 setEventMessages($mesg, null, 'errors');
2227 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
2228 $error++;
2229 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
2230 $action = 'create';
2231 }
2232
2233 if (!(GETPOSTINT('situations') > 0)) {
2234 $error++;
2235 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSituation"));
2236 setEventMessages($mesg, null, 'errors');
2237 $action = 'create';
2238 }
2239
2240 if (!$error) {
2241 $result = $object->fetch(GETPOSTINT('situations'));
2242 $object->fk_facture_source = GETPOSTINT('situations');
2244
2245 if (!empty($origin) && !empty($originid)) {
2246 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2247
2248 $object->origin = $origin; // deprecated
2249 $object->origin_type = $origin;
2250 $object->origin_id = $originid;
2251
2252 // retained warranty
2253 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
2254 $retained_warranty = GETPOSTINT('retained_warranty');
2255 if (price2num($retained_warranty) > 0) {
2256 $object->retained_warranty = (float) price2num($retained_warranty);
2257 }
2258
2259 if (GETPOSTINT('retained_warranty_fk_cond_reglement') > 0) {
2260 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
2261 }
2262
2263 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
2264 if (!empty($retained_warranty_date_limit) && $db->jdate($retained_warranty_date_limit)) {
2265 $object->retained_warranty_date_limit = $db->jdate($retained_warranty_date_limit);
2266 }
2267 $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);
2268 }
2269
2270 foreach ($object->lines as $i => &$line) {
2271 $line->fk_prev_id = $line->id;
2272 $line->fetch_optionals();
2273 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
2274 $line->situation_percent = 0; // New situation percent must be 0 (No cumulative)
2275 } else {
2276 $line->situation_percent = $line->get_prev_progress($object->id); // get good progress including credit note
2277 }
2278
2279 // The $line->situation_percent has been modified, so we must recalculate all amounts
2280 $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);
2281 $line->total_ht = (float) $tabprice[0];
2282 $line->total_tva = (float) $tabprice[1];
2283 $line->total_ttc = (float) $tabprice[2];
2284 $line->total_localtax1 = (float) $tabprice[9];
2285 $line->total_localtax2 = (float) $tabprice[10];
2286 $line->multicurrency_total_ht = (float) $tabprice[16];
2287 $line->multicurrency_total_tva = (float) $tabprice[17];
2288 $line->multicurrency_total_ttc = (float) $tabprice[18];
2289
2290 // If fk_remise_except defined we check if the reduction has already been applied
2291 if ($line->fk_remise_except) {
2292 $discount = new DiscountAbsolute($line->db);
2293 $result = $discount->fetch($line->fk_remise_except);
2294 if ($result > 0) {
2295 // Check if discount not already affected to another invoice
2296 if ($discount->fk_facture_line > 0) {
2297 $line->fk_remise_except = 0;
2298 }
2299 }
2300 }
2301 }
2302 }
2303
2304 $object->fetch_thirdparty();
2305 $object->date = $dateinvoice;
2306 $object->date_pointoftax = $date_pointoftax;
2307 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
2308 $object->note = trim(GETPOST('note', 'restricthtml'));
2309 $object->note_private = trim(GETPOST('note', 'restricthtml'));
2310 $object->ref_client = GETPOST('ref_client', 'alpha');
2311 $object->ref_customer = GETPOST('ref_client', 'alpha');
2312 $object->model_pdf = GETPOST('model', 'alpha');
2313 $object->fk_project = GETPOSTINT('projectid');
2314 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
2315 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
2316 $object->fk_account = GETPOSTINT('fk_account');
2317
2318
2319 // Special properties of replacement invoice
2320
2321 $object->situation_counter += 1;
2322
2323 // Set extrafields from the create form BEFORE createFromCurrent(), so they are already
2324 // present when create() inserts them and fires the BILL_CREATE trigger. This avoids firing
2325 // BILL_CREATE a second time just to expose the extrafields (#32217).
2326 $extrafields->fetch_name_optionals_label($object->table_element);
2327 $extrafields->setOptionalsFromPost(null, $object);
2328
2329 $id = $object->createFromCurrent($user);
2330 if ($id <= 0) {
2331 $mesg = $object->error;
2332 } else {
2333 $nextSituationInvoice = new Facture($db);
2334 $nextSituationInvoice->fetch($id);
2335
2336 // Hooks
2337 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id);
2338 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $nextSituationInvoice, $action); // Note that $action and $object may have been
2339 // modified by hook
2340 if ($reshook < 0) {
2341 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2342 $error++;
2343 }
2344 }
2345 }
2346 }
2347
2348 // End of object creation, we show it
2349 if ($id > 0 && !$error) {
2350 if (isModEnabled('category')) {
2351 $categories = GETPOST('categories', 'array');
2352 if (method_exists($object, 'setCategories')) {
2353 $object->setCategories($categories);
2354 }
2355 }
2356
2357 $db->commit();
2358
2359 // Define output language
2360 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && count($object->lines)) {
2361 $outputlangs = $langs;
2362 $newlang = '';
2363 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
2364 $newlang = GETPOST('lang_id', 'aZ09');
2365 }
2366 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2367 if (empty($object->thirdparty)) {
2368 $object->fetch_thirdparty();
2369 }
2370 $newlang = $object->thirdparty->default_lang;
2371 }
2372 if (!empty($newlang)) {
2373 $outputlangs = new Translate("", $conf);
2374 $outputlangs->setDefaultLang($newlang);
2375 $outputlangs->load('products');
2376 }
2377 $model = $object->model_pdf;
2378 $ret = $object->fetch($id); // Reload to get new records
2379
2380 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2381 if ($result < 0) {
2382 setEventMessages($object->error, $object->errors, 'errors');
2383 }
2384 }
2385
2386 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2387 exit();
2388 } else {
2389 $db->rollback();
2390 $action = 'create';
2391 $_GET["origin"] = GETPOST("origin", 'alpha');
2392 $_GET["originid"] = GETPOSTINT("originid");
2393 setEventMessages($object->error, $object->errors, 'errors');
2394 }
2395 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
2396 // Define date start and date end for all line
2397 $alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
2398 $alldate_end = dol_mktime(GETPOSTINT('alldate_endhour'), GETPOSTINT('alldate_endmin'), 0, GETPOSTINT('alldate_endmonth'), GETPOSTINT('alldate_endday'), GETPOSTINT('alldate_endyear'));
2399 foreach ($object->lines as $line) {
2400 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2401 continue;
2402 }
2403 if ($line->product_type == 1) { // only service line
2404 $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $alldate_start, $alldate_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->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice);
2405 }
2406 }
2407 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
2408 // Define vat_rate
2409 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
2410 $vat_rate = str_replace('*', '', $vat_rate);
2411 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
2412 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
2413 foreach ($object->lines as $line) {
2414 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2415 continue;
2416 }
2417 $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $vat_rate, $localtax1_rate, $localtax2_rate, 'HT', $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);
2418 }
2419 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
2420 // Define vat_rate
2421 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
2422 $remise_percent = str_replace('*', '', $remise_percent);
2423 foreach ($object->lines as $line) {
2424 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2425 continue;
2426 }
2427 $tvatx = $line->tva_tx;
2428 if (!empty($line->vat_src_code)) {
2429 $tvatx .= ' ('.$line->vat_src_code.')';
2430 }
2431 $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, (float) $remise_percent, $line->date_start, $line->date_end, $tvatx, $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->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice);
2432 }
2433 } elseif ($action == 'confirm_addtitleline' && $usercancreate) {
2434 // Handling adding a new title line for subtotals module
2435
2436 $langs->load('subtotals');
2437
2438 $desc = GETPOST('subtotallinedesc', 'alphanohtml');
2439 $depth = GETPOSTINT('subtotallinelevel') ?? 1;
2440
2441 $subtotal_options = array();
2442
2443 foreach (Facture::$TITLE_OPTIONS as $option) {
2444 $value = GETPOST($option, 'alphanohtml');
2445 if ($value) {
2446 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
2447 }
2448 }
2449
2450 // Insert line
2451 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
2452
2453 if ($result >= 0) {
2454 if ($result == 0) {
2455 setEventMessages($object->error, $object->errors, 'warnings');
2456 }
2457 $ret = $object->fetch($object->id); // Reload to get new records
2458 $object->fetch_thirdparty();
2459
2460 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2461 // Define output language
2462 $outputlangs = $langs;
2463 $newlang = GETPOST('lang_id', 'alpha');
2464 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2465 $newlang = $object->thirdparty->default_lang;
2466 }
2467 if (!empty($newlang)) {
2468 $outputlangs = new Translate("", $conf);
2469 $outputlangs->setDefaultLang($newlang);
2470 }
2471
2472 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2473 }
2474 } else {
2475 setEventMessages($object->error, $object->errors, 'errors');
2476 }
2477 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2478 exit();
2479 } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) {
2480 // Handling adding a new subtotal line for subtotals module
2481
2482 $langs->load('subtotals');
2483
2484 $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml');
2485 foreach ($object->lines as $line) {
2486 if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) {
2487 $desc = $line->desc;
2488 $depth = -$line->qty;
2489 }
2490 }
2491
2492 $subtotal_options = array();
2493
2494 foreach (Facture::$SUBTOTAL_OPTIONS as $option) {
2495 $value = GETPOST($option, 'alphanohtml');
2496 if ($value) {
2497 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
2498 }
2499 }
2500
2501 // Insert line
2502 if (isset($desc) && isset($depth)) {
2503 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
2504 } else {
2505 $result = -1;
2506 $object->errors[] = $langs->trans("CorrespondingTitleNotFound");
2507 }
2508
2509 if ($result >= 0) {
2510 $ret = $object->fetch($object->id); // Reload to get new records
2511 $object->fetch_thirdparty();
2512
2513 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2514 // Define output language
2515 $outputlangs = $langs;
2516 $newlang = GETPOST('lang_id', 'alpha');
2517 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2518 $newlang = $object->thirdparty->default_lang;
2519 }
2520 if (!empty($newlang)) {
2521 $outputlangs = new Translate("", $conf);
2522 $outputlangs->setDefaultLang($newlang);
2523 }
2524
2525 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2526 }
2527 } else {
2528 setEventMessages($object->error, $object->errors, 'errors');
2529 }
2530 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2531 exit();
2532 } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) {
2533 $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0;
2534 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva);
2535 } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) {
2536 $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0;
2537 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount);
2538 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && !GETPOST('submitforallmark', 'alpha') && $usercancreate) { // Add a new line
2539 $langs->load('errors');
2540 $error = 0;
2541
2542 // Set if we used free entry or predefined product
2543 $predef = '';
2544 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
2545
2546 $price_ht = '';
2547 $price_ht_devise = '';
2548 $price_ttc = '';
2549 $price_ttc_devise = '';
2550
2551 $price_min = '';
2552 $price_min_ttc = '';
2553
2554 if (GETPOST('price_ht') !== '') {
2555 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
2556 }
2557 if (GETPOST('multicurrency_price_ht') !== '') {
2558 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
2559 }
2560 if (GETPOST('price_ttc') !== '') {
2561 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
2562 }
2563 if (GETPOST('multicurrency_price_ttc') !== '') {
2564 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
2565 }
2566
2567 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
2568 if ($prod_entry_mode == 'free') {
2569 $idprod = 0;
2570 } else {
2571 $idprod = GETPOSTINT('idprod');
2572
2573 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
2574 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
2575 $error++;
2576 }
2577 }
2578
2579 $tva_tx = GETPOST('tva_tx', 'alpha');
2580
2581 $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
2582 $remise_percent = (GETPOSTISSET('remise_percent'.$predef) ? price2num(GETPOST('remise_percent'.$predef, 'alpha'), '', 2) : 0);
2583 if (empty($remise_percent)) {
2584 $remise_percent = 0;
2585 }
2586
2587 // Extrafields
2588 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
2589 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
2590 // Unset extrafield
2591 if (is_array($extralabelsline)) {
2592 // Get extra fields
2593 foreach ($extralabelsline as $key => $value) {
2594 unset($_POST["options_".$key.$predef]);
2595 }
2596 }
2597
2598 if ((empty($idprod) || $idprod < 0) && ($price_ht < 0) && ($qty < 0)) {
2599 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2600 $error++;
2601 }
2602 if (!$prod_entry_mode) {
2603 if (GETPOST('type') < 0 && !GETPOST('search_idprod')) {
2604 setEventMessages($langs->trans('ErrorChooseBetweenFreeEntryOrPredefinedProduct'), null, 'errors');
2605 $error++;
2606 }
2607 }
2608 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
2609 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
2610 $error++;
2611 }
2612
2613 // Do not allow negative lines for free products (invite to enter a discount instead)
2614 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0)
2615 && (((float) $price_ht < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht === '')
2616 && (((float) $price_ht_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht_devise === '')
2617 && ((float) $price_ttc < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES') || $price_ttc === '')
2618 && ((float) $price_ttc_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES') || $price_ttc_devise === '')
2619 && $object->type != $object::TYPE_CREDIT_NOTE) { // Unit price can be 0 but not ''
2620 if (((float) $price_ht < 0 || (float) $price_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
2621 $langs->load("errors");
2622 if ($object->type == $object::TYPE_DEPOSIT) {
2623 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
2624 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
2625 } else {
2626 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
2627 }
2628 $error++;
2629 }
2630 }
2631 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 ''
2632 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('UnitPrice')), null, 'errors');
2633 $error++;
2634 }
2635
2636 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
2637 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
2638 $error++;
2639 }
2640 if ($qty == '') {
2641 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2642 $error++;
2643 }
2644 if ($qty < 0 && !getDolGlobalString('INVOICE_ENABLE_NEGATIVE_QTY')) {
2645 $langs->load("errors");
2646 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
2647 $error++;
2648 }
2649
2650 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
2651 if ($combinations = GETPOST('combinations', 'array:alphanohtml')) {
2652 //Check if there is a product with the given combination
2653 $prodcomb = new ProductCombination($db);
2654
2655 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
2656 $idprod = $res->fk_product_child;
2657 } else {
2658 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
2659 $error++;
2660 }
2661 }
2662 }
2663
2664 $price_base_type = null;
2665 if (!$error && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
2666 $ret = $object->fetch($id);
2667 if ($ret < 0) {
2668 dol_print_error($db, $object->error);
2669 exit();
2670 }
2671 $ret = $object->fetch_thirdparty();
2672
2673 // Clean parameters
2674 $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'));
2675 $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'));
2676 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
2677 $tva_npr = "";
2678
2679 // Define special_code for special lines
2680 $special_code = 0;
2681 // if (!GETPOST(qty)) $special_code=3; // Options should not exists on invoices
2682
2683 // Replaces $pu with that of the product
2684 // Replaces $desc with that of the product
2685 // Replaces $base_price_type with that of the product
2686 // Replaces $fk_unit with that of the product
2687 if (!empty($idprod) && $idprod > 0) {
2688 $prod = new Product($db);
2689 $prod->fetch($idprod);
2690
2691 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
2692
2693 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
2694 $pqp = (GETPOSTINT('pbq') ? GETPOSTINT('pbq') : 0);
2695
2696 $datapriceofproduct = $prod->getSellPrice($mysoc, $object->thirdparty, $pqp);
2697
2698 $pu_ht = $datapriceofproduct['pu_ht'];
2699 $pu_ttc = $datapriceofproduct['pu_ttc'];
2700 $price_min = $datapriceofproduct['price_min'];
2701 $price_min_ttc = (isset($datapriceofproduct['price_min_ttc'])) ? $datapriceofproduct['price_min_ttc'] : null;
2702 $price_base_type = empty($datapriceofproduct['price_base_type']) ? 'HT' : $datapriceofproduct['price_base_type'];
2703
2704 //$tva_tx = $datapriceofproduct['tva_tx'];
2705 //$tva_npr = $datapriceofproduct['tva_npr'];
2706 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
2707 $tmpprodvat = price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
2708
2709 // Set unit price to use
2710 // TODO We should not have this
2711 if (!empty($price_ht) || $price_ht === '0') {
2712 $pu_ht = price2num($price_ht, 'MU');
2713 $pu_ttc = price2num((float) $pu_ht * (1 + ($tmpvat / 100)), 'MU');
2714 } elseif (!empty($price_ht_devise) || $price_ht_devise === '0') {
2715 $pu_ht_devise = price2num($price_ht_devise, 'MU');
2716 $pu_ttc_devise = (float) price2num((float) $pu_ht_devise * (1 + ((float) $tmpvat / 100)), 'MU');
2717 $pu_ht = '';
2718 $pu_ttc = '';
2719 } elseif (!empty($price_ttc) || $price_ttc === '0') {
2720 $pu_ttc = price2num($price_ttc, 'MU');
2721 $pu_ht = price2num((float) $pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2722 } elseif (!empty($price_ttc_devise) || (string) $price_ttc_devise === '0') {
2723 $pu_ttc_devise = (float) price2num($price_ttc_devise, 'MU');
2724 $pu_ht_devise = (float) price2num((float) $pu_ttc_devise / (1 + ((float) $tmpvat / 100)), 'MU');
2725 $pu_ht = '';
2726 $pu_ttc = '';
2727 } elseif ($tmpvat != $tmpprodvat) {
2728 // Is this still used ?
2729 if ($price_base_type != 'HT') {
2730 $pu_ht = price2num($pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2731 } else {
2732 $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU');
2733 }
2734 }
2735
2736 $outputlangs = $langs;
2737 $newlang = '';
2738 $desc = '';
2739
2740 // Define output language
2741 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2742 if (/* empty($newlang) && */ GETPOST('lang_id', 'aZ09')) {
2743 $newlang = GETPOST('lang_id', 'aZ09');
2744 }
2745 if (empty($newlang)) {
2746 $newlang = $object->thirdparty->default_lang;
2747 }
2748 if (!empty($newlang)) {
2749 $outputlangs = new Translate("", $conf);
2750 $outputlangs->setDefaultLang($newlang);
2751 $outputlangs->load('products');
2752 }
2753
2754 $desc = (!empty($prod->multilangs [$outputlangs->defaultlang] ["description"])) ? $prod->multilangs [$outputlangs->defaultlang] ["description"] : $prod->description;
2755 } else {
2756 $desc = $prod->description;
2757 }
2758
2759 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
2760 // 'DoNotAutofillButAutoConcat'
2761 $desc = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
2762 } else {
2763 //'AutoFillFormFieldBeforeSubmit' or 'DoNotUseDescriptionOfProdut' => User has already done the modification they want
2764 $desc = $line_desc;
2765 }
2766
2767 // Add custom code and origin country into description
2768 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
2769 $tmptxt = '(';
2770 // Define output language
2771 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2772 if (!empty($prod->customcode)) {
2773 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
2774 }
2775 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2776 $tmptxt .= ' - ';
2777 }
2778 if (!empty($prod->country_code)) {
2779 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $outputlangs, 0);
2780 }
2781 } else {
2782 if (!empty($prod->customcode)) {
2783 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
2784 }
2785 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2786 $tmptxt .= ' - ';
2787 }
2788 if (!empty($prod->country_code)) {
2789 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $langs, 0);
2790 }
2791 }
2792 $tmptxt .= ')';
2793 $desc = dol_concatdesc($desc, $tmptxt);
2794 }
2795
2796 $type = $prod->type;
2797 $fk_unit = $prod->fk_unit;
2798 } else {
2799 if (!empty($price_ht)) {
2800 $pu_ht = price2num($price_ht, 'MU');
2801 } else {
2802 $pu_ht = '';
2803 }
2804 if (!empty($price_ttc)) {
2805 $pu_ttc = price2num($price_ttc, 'MU');
2806 } else {
2807 $pu_ttc = '';
2808 }
2809 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
2810 $tva_tx = str_replace('*', '', $tva_tx);
2811 if (empty($tva_tx)) {
2812 $tva_npr = 0;
2813 }
2814 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2815 $desc = $line_desc;
2816 $type = GETPOST('type');
2817 $fk_unit = GETPOST('units', 'alpha');
2818
2819 if ($pu_ttc && !$pu_ht) {
2820 $price_base_type = 'TTC';
2821 }
2822 }
2823
2824 // Define info_bits
2825 $info_bits = 0;
2826 if ($tva_npr) {
2827 $info_bits |= 0x01;
2828 }
2829
2830 // Local Taxes
2831 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
2832 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
2833
2834 $pu_ht_devise = price2num($price_ht_devise, '', 2);
2835 $pu_ttc_devise = price2num($price_ttc_devise, '', 2);
2836
2837 // Prepare a price equivalent for minimum price check
2838 $pu_equivalent = $pu_ht;
2839 $pu_equivalent_ttc = $pu_ttc;
2840
2841 $currency_tx = $object->multicurrency_tx;
2842
2843 // Check if we have a foreign currency
2844 // If so, we update the pu_equiv as the equivalent price in base currency
2845 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2846 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
2847 }
2848 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2849 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
2850 }
2851
2852 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
2853 /*
2854 if ($pu_equivalent) {
2855 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
2856 $pu_equivalent_ttc = ...
2857 } else {
2858 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
2859 $pu_equivalent_ht = ...
2860 }
2861 */
2862
2863 // Margin
2864 $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
2865 $buyingprice = price2num(GETPOST('buying_price'.$predef) != '' ? GETPOST('buying_price'.$predef) : ''); // If buying_price is '0', we must keep this value
2866
2867
2868 $price2num_pu_ht = price2num($pu_ht);
2869 $price2num_remise_percent = price2num($remise_percent);
2870 $price2num_price_min = price2num($price_min);
2871 $price2num_price_min_ttc = price2num($price_min_ttc);
2872 if (empty($price2num_pu_ht)) {
2873 $price2num_pu_ht = 0;
2874 }
2875 if (empty($price2num_remise_percent)) {
2876 $price2num_remise_percent = 0;
2877 }
2878 if (empty($price2num_price_min)) {
2879 $price2num_price_min = 0;
2880 }
2881 if (empty($price2num_price_min_ttc)) {
2882 $price2num_price_min_ttc = 0;
2883 }
2884
2885 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
2886 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
2887 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2888 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2889 setEventMessages($mesg, null, 'errors');
2890 $error++;
2891 } 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') {
2892 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2893 setEventMessages($mesg, null, 'errors');
2894 $error++;
2895 }
2896 }
2897
2898 if (!$error) {
2899 /* Disable strange code that use $lines[$i] that is not defined
2900 '@phan-var-force CommonObjectLine[] $lines';
2901 // Add batchinfo if the detail_batch array is defined
2902 if (isModEnabled('productbatch') && !empty($lines[$i]->detail_batch) && is_array($lines[$i]->detail_batch) && getDolGlobalString('INVOICE_INCUDE_DETAILS_OF_LOTS_SERIALS')) {
2903 $langs->load('productbatch');
2904 foreach ($lines[$i]->detail_batch as $batchline) {
2905 $desc .= ' '.$langs->trans('Batch').' '.$batchline->batch.' '.$langs->trans('printQty', $batchline->qty).' ';
2906 }
2907 }
2908 */
2909
2910 // Insert line
2911 $situation_percent = (GETPOSTISSET('progress') ? GETPOSTINT('progress') : 100);
2912
2913 $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);
2914
2915 if ($result > 0) {
2916 $ret = $object->fetch($id); // Reload to get new records
2917 // Define output language and generate document
2918 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2919 $outputlangs = $langs;
2920 $newlang = '';
2921 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
2922 $newlang = GETPOST('lang_id', 'aZ09');
2923 }
2924 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2925 $newlang = $object->thirdparty->default_lang;
2926 }
2927 if (!empty($newlang)) {
2928 $outputlangs = new Translate("", $conf);
2929 $outputlangs->setDefaultLang($newlang);
2930 $outputlangs->load('products');
2931 }
2932 $model = $object->model_pdf;
2933 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2934 if ($result < 0) {
2935 setEventMessages($object->error, $object->errors, 'errors');
2936 }
2937 }
2938
2939 unset($_POST['prod_entry_mode']);
2940 unset($_POST['qty']);
2941 unset($_POST['type']);
2942 unset($_POST['remise_percent']);
2943 unset($_POST['price_ht']);
2944 unset($_POST['multicurrency_price_ht']);
2945 unset($_POST['price_ttc']);
2946 unset($_POST['tva_tx']);
2947 unset($_POST['product_ref']);
2948 unset($_POST['product_label']);
2949 unset($_POST['product_desc']);
2950 unset($_POST['fournprice']);
2951 unset($_POST['buying_price']);
2952 unset($_POST['np_marginRate']);
2953 unset($_POST['np_markRate']);
2954 unset($_POST['dp_desc']);
2955 unset($_POST['idprod']);
2956 unset($_POST['units']);
2957 unset($_POST['date_starthour']);
2958 unset($_POST['date_startmin']);
2959 unset($_POST['date_startsec']);
2960 unset($_POST['date_startday']);
2961 unset($_POST['date_startmonth']);
2962 unset($_POST['date_startyear']);
2963 unset($_POST['date_endhour']);
2964 unset($_POST['date_endmin']);
2965 unset($_POST['date_endsec']);
2966 unset($_POST['date_endday']);
2967 unset($_POST['date_endmonth']);
2968 unset($_POST['date_endyear']);
2969 unset($_POST['situations']);
2970 unset($_POST['progress']);
2971 } else {
2972 setEventMessages($object->error, $object->errors, 'errors');
2973 }
2974
2975 $action = '';
2976 }
2977 }
2978 } elseif ($action == 'addline' && $usercancreate && (
2979 (GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '') ||
2980 (GETPOST('submitforallmark', 'alpha') && GETPOST('markforalllines', 'alpha') !== '')
2981 )) {
2982 $outlangs = $langs;
2983 $margin_rate = GETPOSTISSET('marginforalllines') ? GETPOST('marginforalllines', 'int') : '';
2984 $mark_rate = GETPOSTISSET('markforalllines') ? GETPOST('markforalllines', 'int') : '';
2985 foreach ($object->lines as &$line) {
2986 if ($line->subprice > 0) {
2987 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
2988 continue;
2989 }
2990 $subprice_multicurrency = $line->subprice;
2991 if (is_numeric($margin_rate) && $margin_rate > 0) {
2992 $line->subprice = (float) price2num((float) $line->pa_ht * (1 + (float) $margin_rate / 100), 'MU');
2993 } elseif (is_numeric($mark_rate) && $mark_rate > 0) {
2994 $line->subprice = (float) ($line->pa_ht / (1 - ((float) $mark_rate / 100)));
2995 } else {
2996 $line->subprice = (float) $line->pa_ht;
2997 }
2998
2999 $prod = new Product($db);
3000 $res = $prod->fetch($line->fk_product);
3001 if ($res > 0) {
3002 if ($prod->price_min > $line->subprice) {
3003 $price_subprice = price($line->subprice, 0, $outlangs, 1, -1, -1, 'auto');
3004 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
3005 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
3006 } else {
3007 setEventMessages($prod->error, $prod->errors, 'errors');
3008 }
3009 } else {
3010 setEventMessages($prod->error, $prod->errors, 'errors');
3011 }
3012 // Manage $line->subprice and $line->multicurrency_subprice
3013 $multicurrency_subprice = (float) $line->subprice * $line->multicurrency_subprice / $subprice_multicurrency;
3014 // Update DB
3015 $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);
3016 // Update $object with new margin info
3017 if ($result > 0) {
3018 if (is_numeric($margin_rate) && empty($mark_rate)) {
3019 $line->marge_tx = $margin_rate;
3020 } elseif (is_numeric($mark_rate) && empty($margin_rate)) {
3021 $line->marque_tx = $mark_rate;
3022 }
3023 $line->total_ht = $line->qty * (float) $line->subprice;
3024 $line->total_tva = $line->tva_tx * $line->qty * (float) $line->subprice;
3025 $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $line->subprice;
3026 // Manage $line->subprice and $line->multicurrency_subprice
3027 $line->multicurrency_total_ht = $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3028 $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3029 $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
3030 // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
3031 $line->multicurrency_subprice = $multicurrency_subprice;
3032 } else {
3033 setEventMessages($object->error, $object->errors, 'errors');
3034 }
3035 }
3036 }
3037 } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
3038 // Handling updating a title line for subtotals module
3039
3040 $langs->load('subtotals');
3041
3042 $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title");
3043 $depth = GETPOSTINT('line_depth') ?? 1;
3044
3045 $subtotal_options = array();
3046
3047 foreach (Facture::$TITLE_OPTIONS as $option) {
3048 $value = GETPOST($option, 'alphanohtml');
3049 if ($value) {
3050 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
3051 }
3052 }
3053
3054 // Update line
3055 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
3056
3057 if ($result >= 0) {
3058 if ($result == 0) {
3059 setEventMessages($object->error, $object->errors, 'warnings');
3060 }
3061 $ret = $object->fetch($object->id); // Reload to get new records
3062 $object->fetch_thirdparty();
3063
3064 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3065 // Define output language
3066 $outputlangs = $langs;
3067 $newlang = GETPOST('lang_id', 'alpha');
3068 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3069 $newlang = $object->thirdparty->default_lang;
3070 }
3071 if (!empty($newlang)) {
3072 $outputlangs = new Translate("", $conf);
3073 $outputlangs->setDefaultLang($newlang);
3074 }
3075
3076 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3077 }
3078 } else {
3079 setEventMessages($object->error, $object->errors, 'errors');
3080 }
3081 } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
3082 // Handling updating a subtotal line for subtotals module
3083
3084 $langs->load('subtotals');
3085
3086 $desc = GETPOST('line_desc', 'alphanohtml');
3087 $depth = GETPOSTINT('line_depth');
3088
3089 $subtotal_options = array();
3090
3091 foreach (Facture::$SUBTOTAL_OPTIONS as $option) {
3092 $value = GETPOST($option, 'alphanohtml');
3093 if ($value) {
3094 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
3095 }
3096 }
3097
3098 // Update line
3099 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
3100
3101 if ($result > 0) {
3102 $ret = $object->fetch($object->id); // Reload to get new records
3103 $object->fetch_thirdparty();
3104
3105 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3106 // Define output language
3107 $outputlangs = $langs;
3108 $newlang = GETPOST('lang_id', 'alpha');
3109 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3110 $newlang = $object->thirdparty->default_lang;
3111 }
3112 if (!empty($newlang)) {
3113 $outputlangs = new Translate("", $conf);
3114 $outputlangs->setDefaultLang($newlang);
3115 }
3116
3117 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3118 }
3119 } else {
3120 setEventMessages($object->error, $object->errors, 'errors');
3121 }
3122 } elseif ($action == 'updateline' && $usercancreate && !GETPOST('cancel', 'alpha')) {
3123 if (!$object->fetch($id) > 0) {
3125 }
3126 $object->fetch_thirdparty();
3127
3128 // Clean parameters
3129 $date_start = '';
3130 $date_end = '';
3131 $date_start = dol_mktime(GETPOSTINT('date_starthour'), GETPOSTINT('date_startmin'), GETPOSTINT('date_startsec'), GETPOSTINT('date_startmonth'), GETPOSTINT('date_startday'), GETPOSTINT('date_startyear'));
3132 $date_end = dol_mktime(GETPOSTINT('date_endhour'), GETPOSTINT('date_endmin'), GETPOSTINT('date_endsec'), GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'));
3133 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml') ? GETPOST('product_desc', 'restricthtml') : GETPOST('desc', 'restricthtml'));
3134 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
3135
3136 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
3137 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
3138
3139 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
3140 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
3141
3142 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
3143
3144 // Define info_bits
3145 $info_bits = 0;
3146 if (preg_match('/\*/', $vat_rate)) {
3147 $info_bits |= 0x01;
3148 }
3149
3150 // Define vat_rate
3151 $vat_rate = str_replace('*', '', $vat_rate);
3152 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty);
3153 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty);
3154
3155 // Add buying price
3156 $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
3157 $buyingprice = price2num(GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''); // If buying_price is '0', we must keep this value
3158
3159 // Prepare a price equivalent for minimum price check
3160 $pu_equivalent = $pu_ht;
3161 $pu_equivalent_ttc = $pu_ttc;
3162
3163 $currency_tx = $object->multicurrency_tx;
3164
3165 // Check if we have a foreign currency
3166 // If so, we update the pu_equiv as the equivalent price in base currency
3167 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
3168 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
3169 }
3170 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
3171 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
3172 }
3173
3174 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
3175 /*
3176 if ($pu_equivalent) {
3177 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
3178 $pu_equivalent_ttc = ...
3179 } else {
3180 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
3181 $pu_equivalent_ht = ...
3182 }
3183 */
3184
3185 // Extrafields
3186 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
3187 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
3188 // Unset extrafield
3189 if (is_array($extralabelsline)) {
3190 // Get extra fields
3191 foreach ($extralabelsline as $key => $value) {
3192 unset($_POST["options_".$key]);
3193 }
3194 }
3195
3196 // Define special_code for special lines
3197 $special_code = GETPOSTINT('special_code');
3198 if ($special_code == 3) {
3199 $special_code = 0; // Options should not exists on invoices
3200 }
3201
3202 $line = new FactureLigne($db);
3203 $line->fetch(GETPOSTINT('lineid'));
3204 $percent = $line->get_prev_progress($object->id);
3205 $progress = price2num(GETPOST('progress', 'alpha'));
3206
3207 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->situation_cycle_ref > 0) {
3208 // in case of situation credit note
3209 if ($progress >= 0) {
3210 $mesg = $langs->trans("CantBeNullOrPositive");
3211 setEventMessages($mesg, null, 'warnings');
3212 $error++;
3213 $result = -1;
3214 } elseif ($progress < $line->situation_percent) { // TODO : use a modified $line->get_prev_progress($object->id) result
3215 $mesg = $langs->trans("CantBeLessThanMinPercent");
3216 setEventMessages($mesg, null, 'warnings');
3217 $error++;
3218 $result = -1;
3219 } elseif ($progress < $percent) {
3220 $mesg = '<div class="warning">'.$langs->trans("CantBeLessThanMinPercent").'</div>';
3221 setEventMessages($mesg, null, 'warnings');
3222 $error++;
3223 $result = -1;
3224 }
3225 }
3226
3227 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
3228 if (empty($remise_percent)) {
3229 $remise_percent = 0;
3230 }
3231
3232 $price_base_type = 'HT';
3233 $pu = $pu_ht;
3234 if (empty($pu) && !empty($pu_ttc)) {
3235 $pu = $pu_ttc;
3236 $price_base_type = 'TTC';
3237 }
3238
3239 // Check minimum price
3240 $productid = GETPOSTINT('productid');
3241 if (!empty($productid)) {
3242 $product = new Product($db);
3243 $product->fetch($productid);
3244
3245 $type = $product->type;
3246
3247 $price_min = $product->price_min;
3248 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
3249 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
3250 }
3251 $price_min_ttc = $product->price_min_ttc;
3252 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
3253 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
3254 }
3255
3256 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
3257
3258 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
3259 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
3260 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
3261 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
3262 setEventMessages($mesg, null, 'errors');
3263 $error++;
3264 $action = 'editline';
3265 } 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') {
3266 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
3267 setEventMessages($mesg, null, 'errors');
3268 $error++;
3269 $action = 'editline';
3270 }
3271 }
3272 } else {
3273 $type = GETPOST('type');
3274 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
3275
3276 // Check parameters
3277 if (GETPOST('type') < 0) {
3278 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
3279 $error++;
3280 }
3281 }
3282 if ($qty < 0 && !getDolGlobalString('INVOICE_ENABLE_NEGATIVE_QTY')) {
3283 $langs->load("errors");
3284 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
3285 $error++;
3286 }
3287 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 ''
3288 if (($pu_ht < 0 || $pu_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
3289 $langs->load("errors");
3290 if ($object->type == $object::TYPE_DEPOSIT) {
3291 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
3292 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
3293 } else {
3294 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
3295 }
3296 $error++;
3297 } else {
3298 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
3299 $error++;
3300 }
3301 }
3302
3303 // Invoice situation
3304 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
3305 $previousprogress = $line->getAllPrevProgress($line->fk_facture);
3306 $fullprogress = (float) price2num(GETPOST('progress', 'alpha'), 2);
3307
3308 if ($fullprogress < $previousprogress) {
3309 $error++;
3310 setEventMessages($langs->trans('CantBeLessThanMinPercent'), null, 'errors');
3311 }
3312
3313 // Max 100%
3314 if ($fullprogress > 100) {
3315 $fullprogress = 100;
3316 }
3317 $addprogress = $fullprogress - $previousprogress;
3318 } else {
3319 $addprogress = price2num(GETPOST('progress', 'alpha'));
3320 }
3321
3322 // Update line
3323 if (!$error) {
3324 if (empty($usercancreatemargin)) {
3325 foreach ($object->lines as &$line) {
3326 if ($line->id == GETPOSTINT('lineid')) {
3327 $fournprice = $line->fk_fournprice;
3328 $buyingprice = $line->pa_ht;
3329 break;
3330 }
3331 }
3332 }
3333
3334 $result = $object->updateline(
3335 GETPOSTINT('lineid'),
3336 $description,
3337 (float) $pu,
3338 (float) $qty,
3339 (float) $remise_percent,
3341 $date_end,
3342 $vat_rate,
3343 $localtax1_rate,
3344 $localtax2_rate,
3345 $price_base_type,
3346 $info_bits,
3347 $type,
3348 GETPOSTINT('fk_parent_line'),
3349 0,
3350 (int) $fournprice,
3351 $buyingprice,
3352 $label,
3353 $special_code,
3354 $array_options,
3355 $addprogress,
3356 GETPOSTINT('units'),
3357 (float) $pu_ht_devise
3358 );
3359
3360 if ($result >= 0) {
3361 $ret = $object->fetch($id); // Reload to get new records
3362 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
3363 // Define output language
3364 $outputlangs = $langs;
3365 $newlang = '';
3366 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
3367 $newlang = GETPOST('lang_id', 'aZ09');
3368 }
3369 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
3370 $newlang = $object->thirdparty->default_lang;
3371 }
3372 if (!empty($newlang)) {
3373 $outputlangs = new Translate("", $conf);
3374 $outputlangs->setDefaultLang($newlang);
3375 $outputlangs->load('products');
3376 }
3377 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
3378 }
3379
3380 unset($_POST['qty']);
3381 unset($_POST['type']);
3382 unset($_POST['productid']);
3383 unset($_POST['remise_percent']);
3384 unset($_POST['price_ht']);
3385 unset($_POST['multicurrency_price_ht']);
3386 unset($_POST['price_ttc']);
3387 unset($_POST['tva_tx']);
3388 unset($_POST['product_ref']);
3389 unset($_POST['product_label']);
3390 unset($_POST['product_desc']);
3391 unset($_POST['fournprice']);
3392 unset($_POST['buying_price']);
3393 unset($_POST['np_marginRate']);
3394 unset($_POST['np_markRate']);
3395 unset($_POST['dp_desc']);
3396 unset($_POST['idprod']);
3397 unset($_POST['units']);
3398 unset($_POST['date_starthour']);
3399 unset($_POST['date_startmin']);
3400 unset($_POST['date_startsec']);
3401 unset($_POST['date_startday']);
3402 unset($_POST['date_startmonth']);
3403 unset($_POST['date_startyear']);
3404 unset($_POST['date_endhour']);
3405 unset($_POST['date_endmin']);
3406 unset($_POST['date_endsec']);
3407 unset($_POST['date_endday']);
3408 unset($_POST['date_endmonth']);
3409 unset($_POST['date_endyear']);
3410 unset($_POST['situations']);
3411 unset($_POST['progress']);
3412 } else {
3413 setEventMessages($object->error, $object->errors, 'errors');
3414 }
3415 }
3416 } elseif ($action == 'updatealllines' && $usercancreate && GETPOST('all_percent') == $langs->trans('Modify')) { // Update all lines of situation invoice
3417 if (!$object->fetch($id) > 0) {
3419 }
3420 if (GETPOST('all_progress') != "") {
3421 $all_progress = GETPOSTFLOAT('all_progress');
3422 if ($all_progress > 100) {
3423 $all_progress = 100;
3424 }
3425
3426 foreach ($object->lines as $line) {
3427 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
3428 $percent = $line->getAllPrevProgress($object->id);
3429 } else {
3430 $percent = $line->get_prev_progress($object->id);
3431 }
3432 if ($object->type != $object::TYPE_CREDIT_NOTE && (float) $all_progress < (float) $percent) {
3433 $mesg = $langs->trans("Line").' '.$line->rang.' : '.$langs->trans("CantBeLessThanMinPercent");
3434 setEventMessages($mesg, null, 'warnings');
3435 $result = -1;
3436 } elseif ($object->type == $object::TYPE_CREDIT_NOTE && (float) $all_progress > (float) $percent) {
3437 $mesg = $langs->trans("Line").' '.$line->rang.' : '.$langs->trans("CantBeMoreThanMinPercent");
3438 setEventMessages($mesg, null, 'warnings');
3439 $result = -1;
3440 } else {
3441 $object->update_percent($line, $all_progress, false);
3442 }
3443 }
3444 $object->update_price(1);
3445 }
3446 } elseif ($action == 'updateline' && $usercancreate && !$cancel) {
3447 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id); // To show again edited page
3448 exit();
3449 } elseif ($action == 'confirm_situationout' && $confirm == 'yes' && $usercancreate) {
3450 // Outing situation invoice from cycle
3451 $object->fetch($id, '', '', 0, true);
3452
3453 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
3454 && $object->isSituationInvoice()
3455 && $usercancreate
3456 && !$objectidnext
3457 && $object->is_last_in_cycle()
3458 && $usercanunvalidate
3459 ) {
3460 $outingError = 0;
3461 $newCycle = $object->newCycle(); // we need to keep the "situation behavior" so we place it on a new situation cycle
3462 if ($newCycle > 1) {
3463 // Search credit notes
3464 $lastCycle = $object->situation_cycle_ref;
3465 $lastSituationCounter = $object->situation_counter;
3466 $linkedCreditNotesList = array();
3467
3468 if (count($object->tab_next_situation_invoice) > 0) {
3469 foreach ($object->tab_next_situation_invoice as $next_invoice) {
3470 if ($next_invoice->type == Facture::TYPE_CREDIT_NOTE
3471 && $next_invoice->situation_counter == $object->situation_counter
3472 && $next_invoice->fk_facture_source == $object->id
3473 ) {
3474 $linkedCreditNotesList[] = $next_invoice->id;
3475 }
3476 }
3477 }
3478
3479 $object->situation_cycle_ref = $newCycle;
3480 $object->situation_counter = 1;
3481 $object->situation_final = 0;
3482 if ($object->update($user) > 0) {
3483 $errors = 0;
3484 if (count($linkedCreditNotesList) > 0) {
3485 // now, credit note must follow
3486 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
3487 $sql .= ' SET situation_cycle_ref = '.((int) $newCycle);
3488 $sql .= ' , situation_final=0';
3489 $sql .= ' , situation_counter='.((int) $object->situation_counter);
3490 $sql .= ' WHERE rowid IN ('.$db->sanitize(implode(',', $linkedCreditNotesList)).')';
3491
3492 $resql = $db->query($sql);
3493 if (!$resql) {
3494 $errors++;
3495 }
3496
3497 // Change each progression percent on each lines
3498 foreach ($object->lines as $line) {
3499 // no processing for special product
3500 if ($line->product_type == 9) {
3501 continue;
3502 }
3503
3504
3505 if (!empty($object->tab_previous_situation_invoice)) {
3506 // search the last invoice in cycle
3507 $lineIndex = count($object->tab_previous_situation_invoice) - 1;
3508 $searchPreviousInvoice = true;
3509 while ($searchPreviousInvoice) {
3510 if ($object->tab_previous_situation_invoice[$lineIndex]->isSituationInvoice() || $lineIndex < 1) {
3511 $searchPreviousInvoice = false; // find, exit;
3512 break;
3513 } else {
3514 $lineIndex--; // go to previous invoice in cycle
3515 }
3516 }
3517
3518
3519 $maxPrevSituationPercent = 0;
3520 foreach ($object->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
3521 if ($prevLine->id == $line->fk_prev_id) {
3522 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
3523 }
3524 }
3525
3526
3527 $line->situation_percent -= $maxPrevSituationPercent;
3528
3529 if ($line->update() < 0) {
3530 $errors++;
3531 }
3532 }
3533 }
3534 }
3535
3536 if (!$errors) {
3537 setEventMessages($langs->trans('Updated'), null, 'mesgs');
3538 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
3539 } else {
3540 setEventMessages($langs->trans('ErrorOutingSituationInvoiceCreditNote'), array(), 'errors');
3541 }
3542 } else {
3543 setEventMessages($langs->trans('ErrorOutingSituationInvoiceOnUpdate'), array(), 'errors');
3544 }
3545 } else {
3546 setEventMessages($langs->trans('ErrorFindNextSituationInvoice'), array(), 'errors');
3547 }
3548 }
3549 } elseif ($action == 'import_lines_from_object' && $usercancreate && $object->status == Facture::STATUS_DRAFT
3551 // add lines from objectlinked
3552 $fromElement = GETPOST('fromelement');
3553 $fromElementid = GETPOST('fromelementid');
3554 $importLines = GETPOST('line_checkbox');
3555
3556 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && ctype_alpha($fromElement) && !empty($fromElementid)) {
3557 $lineClassName = '';
3558 if ($fromElement == 'commande') {
3559 dol_include_once('/'.$fromElement.'/class/'.$fromElement.'.class.php');
3560 $lineClassName = 'OrderLine';
3561 } elseif ($fromElement == 'propal') {
3562 dol_include_once('/comm/'.$fromElement.'/class/'.$fromElement.'.class.php');
3563 $lineClassName = 'PropaleLigne';
3564 }
3565 $nextRang = count($object->lines) + 1;
3566 $importCount = 0;
3567 $error = 0;
3568 foreach ($importLines as $lineId) {
3569 if ($lineClassName === '') {
3570 // No class
3571 if ($error === 0) {
3572 // Log only once
3573 dol_syslog('compta/facture/card - No lineClassName - skip import', LOG_ERR);
3574 }
3575 // Ensure we report that all line failed (see error message below)
3576 $error++;
3577 break;
3578 }
3579
3580 $lineId = intval($lineId);
3581 $originLine = new $lineClassName($db);
3582 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
3583 $originLine->fetch_optionals();
3584 $desc = $originLine->desc;
3585 $pu_ht = $originLine->subprice;
3586 $qty = $originLine->qty;
3587 $txtva = $originLine->tva_tx;
3588 $txlocaltax1 = $originLine->localtax1_tx;
3589 $txlocaltax2 = $originLine->localtax2_tx;
3590 $fk_product = $originLine->fk_product;
3591 $remise_percent = $originLine->remise_percent;
3592 $date_start = $originLine->date_start;
3593 $date_end = $originLine->date_end;
3594 $fk_code_ventilation = 0;
3595 $info_bits = $originLine->info_bits;
3596 $fk_remise_except = $originLine->fk_remise_except;
3597 $price_base_type = 'HT';
3598 $pu_ttc = 0;
3599 $type = $originLine->product_type;
3600 $rang = $nextRang++;
3601 $special_code = $originLine->special_code;
3602 $origin = $originLine->element;
3603 $origin_id = $originLine->id;
3604 $fk_parent_line = 0;
3605 $fk_fournprice = $originLine->fk_fournprice;
3606 $pa_ht = $originLine->pa_ht;
3607 $label = $originLine->label;
3608 $array_options = $originLine->array_options;
3609 if ($object->isSituationInvoice()) {
3610 $situation_percent = 0;
3611 } else {
3612 $situation_percent = 100;
3613 }
3614 $fk_prev_id = 0;
3615 $fk_unit = $originLine->fk_unit;
3616 $pu_ht_devise = $originLine->multicurrency_subprice;
3617
3618 $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);
3619
3620 if ($res > 0) {
3621 $importCount++;
3622 } else {
3623 $error++;
3624 }
3625 } else {
3626 $error++;
3627 }
3628 }
3629
3630 if ($error) {
3631 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
3632 }
3633 }
3634 }
3635
3636
3637 // Actions when printing a doc from card
3638 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
3639
3640 // Actions to send emails
3641 if (empty($id)) {
3642 $id = $facid;
3643 }
3644 if (!empty($object->id) && $action == 'send') { // Test on permission not required
3645 // load totalpaid, totaldeposits, totalcreditnotes that can be used in email templates
3646 $object->getSommePaiement(-1);
3647 $object->getSumCreditNotesUsed(-1);
3648 $object->getSumDepositsUsed(-1);
3649 }
3650 $triggersendname = 'BILL_SENTBYMAIL';
3651 $paramname = 'id';
3652 $autocopy = 'MAIN_MAIL_AUTOCOPY_INVOICE_TO';
3653 $trackid = 'inv'.$object->id;
3654 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
3655
3656 // Actions to build doc
3657 $upload_dir = $conf->invoice->multidir_output[!empty($object->entity) ? $object->entity : $conf->entity];
3658 $permissiontoadd = $usercancreate;
3659 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
3660
3661
3662 if ($action == 'update_extras' && $permissiontoeditextra) {
3663 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
3664
3665 $attribute_name = GETPOST('attribute', 'aZ09');
3666
3667 // Fill array 'array_options' with data from add form
3668 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
3669 if ($ret < 0) {
3670 $error++;
3671 }
3672
3673 if (!$error) {
3674 // Actions on extra fields
3675 $result = $object->updateExtraField($attribute_name, 'BILL_MODIFY');
3676 if ($result < 0) {
3677 setEventMessages($object->error, $object->errors, 'errors');
3678 $error++;
3679 }
3680 }
3681
3682 if ($error) {
3683 $action = 'edit_extras';
3684 }
3685 }
3686
3687 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3688 if ($action == 'addcontact' && $usercancreate) {
3689 $result = $object->fetch($id);
3690
3691 if ($result > 0 && $id > 0) {
3692 $contactid = (GETPOST('userid') ? GETPOSTINT('userid') : GETPOSTINT('contactid'));
3693 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
3694 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
3695 }
3696
3697 if ($result >= 0) {
3698 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3699 exit();
3700 } else {
3701 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
3702 $langs->load("errors");
3703 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
3704 } else {
3705 setEventMessages($object->error, $object->errors, 'errors');
3706 }
3707 }
3708 } elseif ($action == 'swapstatut' && $usercancreate) {
3709 // toggle the status of a contact
3710 if ($object->fetch($id)) {
3711 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
3712 } else {
3714 }
3715 } elseif ($action == 'deletecontact' && $usercancreate) {
3716 // Delete a contact
3717 $object->fetch($id);
3718 $result = $object->delete_contact($lineid);
3719
3720 if ($result >= 0) {
3721 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3722 exit();
3723 } else {
3725 }
3726 }
3727
3728 if ($error) {
3729 $action = 'edit_extras';
3730 }
3731 }
3732}
3733
3734
3735/*
3736 * View
3737 */
3738
3739$form = new Form($db);
3740$formother = new FormOther($db);
3741$formfile = new FormFile($db);
3742$formmargin = new FormMargin($db);
3743$soc = new Societe($db);
3744$paymentstatic = new Paiement($db);
3745$bankaccountstatic = new Account($db);
3746$formproject = null;
3747if (isModEnabled('project')) {
3748 $formproject = new FormProjets($db);
3749}
3750
3751$now = dol_now();
3752
3753$title = $object->ref." - ".$langs->trans('Card');
3754if ($action == 'create') {
3755 $title = $langs->trans("NewBill");
3756}
3757$help_url = "EN:Customers_Invoices|FR:Factures_Clients|ES:Facturas_a_clientes";
3758
3759llxHeader('', $title, $help_url);
3760
3761// Mode creation
3762
3763if ($action == 'create') {
3764 $facturestatic = new Facture($db);
3765 $extrafields->fetch_name_optionals_label($facturestatic->table_element);
3766
3767 print load_fiche_titre($langs->trans('NewBill'), '', 'bill');
3768
3769 if ($socid > 0) {
3770 $res = $soc->fetch($socid);
3771 }
3772
3773 $currency_code = $conf->currency;
3774
3775 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
3776 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
3777 $fk_account = GETPOSTINT('fk_account');
3778
3779 $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
3780 if (empty($dateinvoice)) {
3781 $dateinvoice = (getDolGlobalString('MAIN_DO_NOT_AUTOFILL_DATE_INVOICE') ? -1 : ''); // By default '' so we will autofill date. -1 means keep empty.
3782 }
3783
3784 // Load objectsrc
3785 $objectsrc = null;
3786 if (!empty($origin) && !empty($originid)) {
3787 // Parse element/subelement (ex: project_task)
3788 $element = $subelement = $origin;
3789 $regs = array();
3790 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
3791 $element = $regs[1];
3792 $subelement = $regs[2];
3793 }
3794
3795 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
3796
3797 if ($element == 'project') {
3798 $projectid = $originid;
3799
3800 if (empty($cond_reglement_id)) {
3801 $cond_reglement_id = $soc->cond_reglement_id;
3802 }
3803 if (empty($mode_reglement_id)) {
3804 $mode_reglement_id = $soc->mode_reglement_id;
3805 }
3806 if (empty($fk_account)) {
3807 $fk_account = $soc->fk_account;
3808 }
3809 } else {
3810 // For compatibility
3811 if ($element == 'order' || $element == 'commande') {
3812 $element = $subelement = 'commande';
3813 }
3814 if ($element == 'propal') {
3815 $element = 'comm/propal';
3816 $subelement = 'propal';
3817 }
3818 if ($element == 'contract') {
3819 $element = $subelement = 'contrat';
3820 }
3821 if ($element == 'shipping') {
3822 $element = $subelement = 'expedition';
3823 }
3824
3825 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
3826
3827 $classname = ucfirst($subelement);
3828 $objectsrc = new $classname($db);
3829 '@phan-var-force Commande|Propal|Contrat|Expedition|Facture $objectsrc';
3831 $objectsrc->fetch($originid);
3832 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
3833 $objectsrc->fetch_lines();
3834 }
3835 $objectsrc->fetch_thirdparty();
3836
3837 $projectid = (!empty($projectid) ? $projectid : $objectsrc->fk_project);
3838
3839 // Propagate ref customer of src object to the invoice ?
3840 if (getDolGlobalString("INVOICE_DO_NOT_PROPAGATE_REF_CUSTOMER_OF_SRC_TO_INVOICE")) {
3841 $ref_client = "";
3842 } else {
3843 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : ''));
3844 }
3845
3846 // Only if socid not filled else it's already done above
3847 if (empty($socid)) {
3848 $soc = $objectsrc->thirdparty;
3849 }
3850
3851 if ($element == 'expedition') {
3852 $elem = $subelem = $objectsrc->origin_type;
3853 $expeoriginid = $objectsrc->origin_id;
3854 dol_include_once('/'.$elem.'/class/'.$subelem.'.class.php');
3855 $classname = ucfirst($subelem);
3856
3857 $expesrc = new $classname($db);
3858 '@phan-var-force Expedition $expesrc';
3860 dol_syslog("Is type Facture|Commande or Expedition: $element...expesrc($classname)=".get_class($expesrc));
3861 $expesrc->fetch($expeoriginid);
3862
3863 $cond_reglement_id = (!empty($expesrc->cond_reglement_id) ? $expesrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 1));
3864 $mode_reglement_id = (!empty($expesrc->mode_reglement_id) ? $expesrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
3865 $fk_account = (!empty($expesrc->fk_account) ? $expesrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : 0));
3866
3867 if (isModEnabled('multicurrency')) {
3868 $currency_code = (!empty($expesrc->multicurrency_code) ? $expesrc->multicurrency_code : (!empty($soc->multicurrency_code) ? $soc->multicurrency_code : $objectsrc->multicurrency_code));
3869 $currency_tx = (!empty($expesrc->multicurrency_tx) ? $expesrc->multicurrency_tx : (!empty($soc->multicurrency_tx) ? $soc->multicurrency_tx : $objectsrc->multicurrency_tx));
3870 }
3871
3872 // replicate input reason
3873 $inputReasonId = (!empty($objectsrc->demand_reason_id) ? $objectsrc->demand_reason_id : (!empty($soc->demand_reason_id) ? $soc->demand_reason_id : 0));
3874
3875 //Replicate extrafields
3876 $expesrc->fetch_optionals();
3877 $object->array_options = $expesrc->array_options;
3878 } else {
3879 $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)));
3880 $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)));
3881 $fk_account = (!empty($objectsrc->fk_account) ? $objectsrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : (!empty($fk_account) ? $fk_account : 0)));
3882
3883 if (isModEnabled('multicurrency')) {
3884 if (!empty($objectsrc->multicurrency_code)) {
3885 $currency_code = $objectsrc->multicurrency_code;
3886 }
3887 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
3888 $currency_tx = $objectsrc->multicurrency_tx;
3889 }
3890 }
3891
3892 // replicate input reason
3893 $inputReasonId = (!empty($objectsrc->demand_reason_id) ? $objectsrc->demand_reason_id : (!empty($soc->demand_reason_id) ? $soc->demand_reason_id : 0));
3894
3895 // Replicate extrafields
3896 $objectsrc->fetch_optionals();
3897 $object->array_options = $objectsrc->array_options;
3898 }
3899 }
3900 } else {
3901 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
3902 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
3903 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
3904 $inputReasonId = empty($soc->demand_reason_id) ? $inputReasonId : $soc->demand_reason_id;
3905
3906 if (isModEnabled('multicurrency') && !empty($soc->multicurrency_code)) {
3907 $currency_code = $soc->multicurrency_code;
3908 }
3909 }
3910
3911 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
3912 if (!GETPOST('changecompany')) {
3913 if (GETPOSTISSET('cond_reglement_id')) {
3914 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
3915 }
3916 if (GETPOSTISSET('mode_reglement_id')) {
3917 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
3918 }
3919 if (GETPOSTISSET('cond_reglement_id')) {
3920 $fk_account = GETPOSTINT('fk_account');
3921 }
3922 }
3923
3924 // when payment condition is empty (means not override by payment condition form a other object, like third-party), try to use default value
3925 if (empty($cond_reglement_id)) {
3926 $cond_reglement_id = GETPOSTINT("cond_reglement_id");
3927 }
3928
3929 // when payment mode is empty (means not override by payment mode form a other object, like third-party), try to use default value
3930 if (empty($mode_reglement_id)) {
3931 $mode_reglement_id = GETPOSTINT("mode_reglement_id");
3932 }
3933
3934 // when bank account is empty (means not override by payment mode form a other object, like third-party), try to use default value
3935 // if ($socid > 0 && $fk_account) { // A company has already been set and it has a default fk_account
3936 // $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
3937 // } else { // No company forced
3938 // $fk_account = GETPOST("fk_account", 'int');
3939 // }
3940
3941 if (!empty($soc->id)) {
3942 $absolute_discount = $soc->getAvailableDiscounts();
3943 }
3944 $note_public = $object->getDefaultCreateValueFor('note_public', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_public : null));
3945 $note_private = $object->getDefaultCreateValueFor('note_private', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_private : null));
3946
3947 if (!empty($conf->use_javascript_ajax)) {
3948 require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3949 print ajax_combobox('fac_replacement');
3950 print ajax_combobox('fac_avoir');
3951 print ajax_combobox('situations');
3952 }
3953
3954 if ($origin == 'contrat') {
3955 $langs->load("admin");
3956 $text = $langs->trans("ToCreateARecurringInvoice");
3957 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGene", $langs->transnoentitiesnoconv("MenuFinancial"), $langs->transnoentitiesnoconv("BillsCustomers"), $langs->transnoentitiesnoconv("ListOfTemplates"));
3958 if (!getDolGlobalString('INVOICE_DISABLE_AUTOMATIC_RECURRING_INVOICE')) {
3959 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGeneAuto", $langs->transnoentitiesnoconv('Module2300Name'));
3960 }
3961 print info_admin($text, 0, 0, 'info', '').'<br>';
3962 }
3963
3964 print '<form name="add" action="'.$_SERVER["PHP_SELF"].'" method="POST" id="formtocreate" name="formtocreate">';
3965 print '<input type="hidden" name="token" value="'.newToken().'">';
3966 print '<input type="hidden" name="action" id="formtocreateaction" value="add">';
3967 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
3968 if ($soc->id > 0) {
3969 print '<input type="hidden" name="socid" value="'.$soc->id.'">'."\n";
3970 }
3971 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
3972 print '<input name="ref" type="hidden" value="provisoire">';
3973 print '<input name="ref_client" type="hidden" value="'.$ref_client.'">';
3974 print '<input name="force_cond_reglement_id" type="hidden" value="0">';
3975 print '<input name="force_mode_reglement_id" type="hidden" value="0">';
3976 print '<input name="force_fk_account" type="hidden" value="0">';
3977 print '<input type="hidden" name="origin" value="'.$origin.'">';
3978 print '<input type="hidden" name="originid" value="'.$originid.'">';
3979 print '<input type="hidden" name="originentity" value="'.GETPOSTINT('originentity').'">';
3980 if (!empty($currency_tx)) {
3981 print '<input type="hidden" name="originmulticurrency_tx" value="'.$currency_tx.'">';
3982 }
3983
3984 print dol_get_fiche_head();
3985
3986 // Call Hook tabContentCreateInvoice
3987 $parameters = array();
3988 // Note that $action and $object may be modified by hook
3989 $reshook = $hookmanager->executeHooks('tabContentCreateInvoice', $parameters, $object, $action);
3990 if (empty($reshook)) {
3991 print '<table class="border centpercent">';
3992
3993 $exampletemplateinvoice = new FactureRec($db);
3994 $invoice_predefined = new FactureRec($db);
3995 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
3996 $invoice_predefined->fetch(GETPOSTINT('fac_rec'));
3997 }
3998
3999 // Ref
4000 /*
4001 print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans('Ref').'</td>';
4002 print '<td colspan="2">';
4003 print $langs->trans("Draft");
4004 print '</td>';
4005 print '</tr>'."\n";
4006 */
4007
4008 // Thirdparty
4009 if ($soc->id > 0 && (!GETPOSTINT('fac_rec') || !empty($invoice_predefined->frequency))) {
4010 // If thirdparty known and not a predefined invoiced without a recurring rule
4011 print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans('Customer').'</td>';
4012 print '<td colspan="2">';
4013 print $soc->getNomUrl(1, 'customer');
4014 print '<input type="hidden" name="socid" value="'.$soc->id.'">';
4015 // Outstanding Bill
4016 $arrayoutstandingbills = $soc->getOutstandingBills();
4017 $outstandingBills = $arrayoutstandingbills['opened'];
4018 print ' - <span class="opacitymedium valignmiddle">'.$langs->trans('CurrentOutstandingBill').':</span> ';
4019 print '<span class="amount valignmiddle">'.price($outstandingBills, 0, $langs, 0, 0, -1, $conf->currency).'</span>';
4020 if ($soc->outstanding_limit != '') {
4021 if ($outstandingBills > $soc->outstanding_limit) {
4022 print img_warning($langs->trans("OutstandingBillReached"));
4023 }
4024 print ' / '.price($soc->outstanding_limit, 0, $langs, 0, 0, -1, $conf->currency);
4025 }
4026 print '</td>';
4027 print '</tr>'."\n";
4028 } else {
4029 print '<tr><td class="fieldrequired">'.$langs->trans('Customer').'</td>';
4030 print '<td colspan="2">';
4031 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
4032 print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company($soc->id, 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 widthcentpercentminusxx maxwidth500');
4033 // Option to reload page to retrieve customer information.
4034 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
4035 print '<script>
4036 $(document).ready(function() {
4037 $("#socid").change(function() {
4038 /*
4039 console.log("Submit page");
4040 $(\'input[name="action"]\').val(\'create\');
4041 $(\'input[name="force_cond_reglement_id"]\').val(\'1\');
4042 $(\'input[name="force_mode_reglement_id"]\').val(\'1\');
4043 $(\'input[name="force_fk_account"]\').val(\'1\');
4044 $("#formtocreate").submit(); */
4045
4046 // For company change, we must submit page with action=create instead of action=add
4047 console.log("We have changed the company - Resubmit page");
4048 jQuery("input[name=changecompany]").val("1");
4049 jQuery("#formtocreateaction").val("create");
4050 jQuery("#formtocreate").submit();
4051 });
4052 });
4053 </script>';
4054 }
4055 if (!GETPOSTINT('fac_rec')) {
4056 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>';
4057 }
4058 print '</td>';
4059 print '</tr>'."\n";
4060 }
4061
4062 // Overwrite some values if creation of invoice is from a predefined invoice
4063 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
4064 //$invoice_predefined->fetch(GETPOSTINT('fac_rec'));
4065 foreach ($invoice_predefined->array_options as $key => $option) {
4066 if (!isset($object->array_options[$key])) {
4067 $object->array_options[$key] = $invoice_predefined->array_options[$key];
4068 }
4069 }
4070
4071 $dateinvoice = $invoice_predefined->date_when; // To use next gen date by default later
4072 if (empty($projectid)) {
4073 $projectid = $invoice_predefined->fk_project;
4074 }
4075 $cond_reglement_id = $invoice_predefined->cond_reglement_id;
4076 $mode_reglement_id = $invoice_predefined->mode_reglement_id;
4077 $fk_account = $invoice_predefined->fk_account;
4078 $note_public = $invoice_predefined->note_public;
4079 $note_private = $invoice_predefined->note_private;
4080
4081 if (!empty($invoice_predefined->multicurrency_code)) {
4082 $currency_code = $invoice_predefined->multicurrency_code;
4083 }
4084 if (!empty($invoice_predefined->multicurrency_tx)) {
4085 $currency_tx = $invoice_predefined->multicurrency_tx;
4086 }
4087
4088 $sql = 'SELECT r.rowid, r.titre as title, r.total_ttc';
4089 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_rec as r';
4090 $sql .= ' WHERE r.fk_soc = '.((int) $invoice_predefined->socid);
4091
4092 $resql = $db->query($sql);
4093 if ($resql) {
4094 $num = $db->num_rows($resql);
4095 $i = 0;
4096
4097 if ($num > 0) {
4098 print '<tr><td>'.$langs->trans('CreateFromRepeatableInvoice').'</td><td>';
4099 //print '<input type="hidden" name="fac_rec" id="fac_rec" value="'.GETPOST('fac_rec', 'int').'">';
4100 print '<select class="flat" id="fac_rec" name="fac_rec">'; // We may want to change the template to use
4101 print '<option value="0" selected></option>';
4102 while ($i < $num) {
4103 $objp = $db->fetch_object($resql);
4104 print '<option value="'.$objp->rowid.'"';
4105 if (GETPOSTINT('fac_rec') == $objp->rowid) {
4106 print ' selected';
4107 $exampletemplateinvoice->fetch(GETPOSTINT('fac_rec'));
4108 }
4109 print '>'.$objp->title.' ('.price($objp->total_ttc).' '.$langs->trans("TTC").')</option>';
4110 $i++;
4111 }
4112 print '</select>';
4113
4114 print ajax_combobox("fac_rec");
4115
4116 // Option to reload page to retrieve customer information. Note, this clear other input
4117 if (!getDolGlobalString('RELOAD_PAGE_ON_TEMPLATE_CHANGE_DISABLED')) {
4118 print '<script type="text/javascript">
4119 $(document).ready(function() {
4120 $("#fac_rec").change(function() {
4121 console.log("We have changed the template invoice - Reload page");
4122 var fac_rec = $(this).val();
4123 var socid = $(\'#socid\').val();
4124 // 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.
4125 window.location.href = "'.$_SERVER["PHP_SELF"].'?action=create&socid="+socid+"&fac_rec="+fac_rec;
4126 });
4127 });
4128 </script>';
4129 }
4130 print '</td></tr>';
4131 }
4132 $db->free($resql);
4133 } else {
4135 }
4136 }
4137
4138 print '<tr><td class="tdtop fieldrequired titlefieldcreate">'.$langs->trans('Type').'</td><td colspan="2">';
4139 print '<div class="listofinvoicetypetable">'."\n";
4140
4141 // Standard invoice
4142 print '<div class="listofinvoicetype"><div class="">';
4143 $tmp = '<input type="radio" id="radio_standard" name="type" value="0"'.(GETPOSTINT('type') ? '' : ' checked').'> ';
4144 $tmp .= '<label for="radio_standard" >'.$langs->trans("InvoiceStandardAsk");
4145 $tmp .= ' <span class="opacitymedium">'.$langs->trans("InvoiceStandardAsk2").'</span>';
4146 $tmp .= '</label>';
4147 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4148 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceStandardDesc").'<br><br>'.$langs->transnoentities("YouMustCreateStandardInvoiceFirstDesc"), 1, 'help', 'nowraponall', 0, 3, 'standardonsmartphone');
4149 print $desc;
4150 if ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid))) {
4151 /*print '<td class="nowrap" style="padding-left: 5px">';
4152 $arraylist = array(
4153 //'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
4154 //'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
4155 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4156 );
4157 print $form->selectarray('typestandard', $arraylist, GETPOST('typestandard', 'aZ09'), 0, 0, 0, '', 1);
4158 print '</td>';*/
4159 if (!getDolGlobalInt('INVOICE_DEPOSIT_INVOICE_ONLY_SAME_LINES')) {
4160 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%') . '"/>';
4161 }
4162 }
4163 print '</div></div>'."\n";
4164
4165 if ((empty($origin)) || ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid)))) {
4166 // Deposit - Down payment
4167 if (!getDolGlobalString('INVOICE_DISABLE_DEPOSIT')) {
4168 print '<div class="listofinvoicetype"><div class="">';
4169 $tmp = '<input type="radio" id="radio_deposit" name="type" value="3"'.(GETPOSTINT('type') == 3 ? ' checked' : '').'> ';
4170 print '<script type="text/javascript">
4171 jQuery(document).ready(function() {
4172 jQuery("#typestandardinvoice, #valuestandardinvoice").click(function() {
4173 jQuery("#radio_standard").prop("checked", true);
4174 });
4175 jQuery("#typedeposit, #valuedeposit").click(function() {
4176 jQuery("#radio_deposit").prop("checked", true);
4177 });
4178 jQuery("#typedeposit").change(function() {
4179 console.log("We change type of down payment: "+jQuery("#typedeposit").val());
4180
4181 if (jQuery("#typedeposit").val() == "amount") {
4182 jQuery("#valuedeposit").attr("placeholder", "'.$langs->getCurrencySymbol($conf->currency).'");
4183 jQuery("#valuedeposit").val("");
4184 } else {
4185 jQuery("#valuedeposit").attr("placeholder", "50%");
4186 jQuery("#valuedeposit").val("");
4187 }
4188
4189 jQuery("#radio_deposit").prop("checked", true);
4190 setRadioForTypeOfInvoice();
4191 });
4192 jQuery("#radio_standard, #radio_deposit, #radio_replacement, #radio_creditnote, #radio_template").change(function() {
4193 setRadioForTypeOfInvoice();
4194 });
4195
4196 function setRadioForTypeOfInvoice() {
4197 console.log("Change radio for type of invoice");
4198 if (jQuery("#radio_deposit").prop("checked") && (jQuery("#typedeposit").val() == \'amount\' || jQuery("#typedeposit").val() == \'variable\')) {
4199 jQuery("#checkforselects").prop("disabled", true);
4200 jQuery("#checkforselects").prop("checked", false);
4201 jQuery(".checkforselect").prop("disabled", true);
4202 jQuery(".checkforselect").prop("checked", false);
4203 } else {
4204 jQuery("#checkforselects").prop("disabled", false);
4205 jQuery("#checkforselects").prop("checked", true);
4206 jQuery(".checkforselect").prop("disabled", false);
4207 jQuery(".checkforselect").prop("checked", true);
4208 }
4209 }
4210 });
4211 </script>';
4212
4213 $tmp = $tmp.'<label for="radio_deposit">'.$langs->trans("InvoiceDeposit").'</label>';
4214 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4215 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceDepositDesc"), 1, 'help', '', 0, 3, 'depositonsmartphone');
4216 print $desc;
4217 if (($origin == 'propal') || ($origin == 'commande')) {
4218 if (!getDolGlobalInt('INVOICE_DEPOSIT_INVOICE_ONLY_SAME_LINES')) {
4219 $arraylist = array(
4220 'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
4221 'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
4222 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4223 );
4224 } else {
4225 $arraylist = array(
4226 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
4227 );
4228 }
4229
4230 $typedeposit = GETPOST('typedeposit', 'aZ09');
4231 $valuedeposit = GETPOSTINT('valuedeposit');
4232 if (empty($typedeposit) && !empty($objectsrc->deposit_percent)) {
4233 $origin_payment_conditions_deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $objectsrc->cond_reglement_id);
4234 if (!empty($origin_payment_conditions_deposit_percent)) {
4235 $typedeposit = 'variable';
4236 }
4237 }
4238 if (empty($valuedeposit) && $typedeposit == 'variable' && !empty($objectsrc->deposit_percent)) {
4239 $valuedeposit = $objectsrc->deposit_percent;
4240 }
4241 print '<span class="marginleftonly">'.$form->selectarray('typedeposit', $arraylist, $typedeposit, 0, 0, 0, '', 1).'</span>';
4242 //print '<span class="opacitymedium paddingleft">'.$langs->trans("AmountOrPercent").'</span>';
4243 print '<input type="text" id="valuedeposit" name="valuedeposit" class="width75 right" value="'.($valuedeposit ? $valuedeposit : '').'"'.($valuedeposit ? '' : 'placeholder="'.$langs->getCurrencySymbol($conf->currency).'"').'>';
4244 }
4245
4246 print '</div></div>'."\n";
4247 }
4248 }
4249
4250 if ($socid > 0) {
4251 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
4252 // First situation invoice
4253 print '<div class="listofinvoicetype"><div class="">';
4254 $tmp = '<input id="radio_situation" type="radio" name="type" value="5"'.(GETPOST('type') == 5 ? ' checked' : '').'> ';
4255 $tmp = $tmp.'<label for="radio_situation" >'.$langs->trans("InvoiceFirstSituationAsk").'</label>';
4256 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
4257 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceFirstSituationDesc"), 1, 'help', '', 0, 3, 'firstsituationonsmartphone');
4258 print $desc;
4259
4260 // Next situation invoice
4261 $opt = $form->selectSituationInvoices((string) GETPOSTINT('originid'), $socid);
4262
4263 //print ' &nbsp; ';
4264 print '</div></div><div class="listofinvoicetype"><div>';
4265
4266 $tmp = '<input id="radio_situation_bis" type="radio" name="type" value="5"'.(GETPOST('type') == 5 && GETPOSTINT('originid') ? ' checked' : '');
4267 if ($opt == ('<option value="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
4268 $tmp .= ' disabled';
4269 }
4270 $tmp .= '> ';
4271 $text = $tmp.'<label for="radio_situation_bis">'.$langs->trans("InvoiceSituationAsk").'</label> ';
4272
4273 $text .= '<select class="flat minwidth125" id="situations" name="situations"';
4274 if ($opt == ('<option value="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
4275 $text .= ' disabled';
4276 }
4277 $text .= '>';
4278 $text .= $opt;
4279 $text .= '</select>';
4280 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceSituationDesc"), 1, 'help', '', 0, 3);
4281 print $desc;
4282 print '</div></div>'."\n";
4283 }
4284
4285 // Replacement
4286 if (!getDolGlobalString('INVOICE_DISABLE_REPLACEMENT')) {
4287 // Type de facture
4288 $facids = $facturestatic->list_replacable_invoices($soc->id);
4289 if ($facids < 0) {
4290 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
4291 exit();
4292 }
4293 $options = "";
4294 if (is_array($facids)) {
4295 foreach ($facids as $facparam) {
4296 $options .= '<option value="'.$facparam ['id'].'"';
4297 if ($facparam['id'] == GETPOSTINT('fac_replacement')) {
4298 $options .= ' selected';
4299 }
4300 $options .= '>'.$facparam['ref'];
4301 $options .= ' ('.$facturestatic->LibStatut($facparam['paid'], $facparam['status'], 0, $facparam['alreadypaid']).')';
4302 $options .= '</option>';
4303 }
4304 }
4305
4306 print '<!-- replacement line -->';
4307 print '<div class="listofinvoicetype"><div class="">';
4308 $tmp = '<input type="radio" name="type" id="radio_replacement" value="1"'.(GETPOST('type') == 1 ? ' checked' : '');
4309 if (!$options || $invoice_predefined->id > 0) {
4310 $tmp .= ' disabled';
4311 }
4312 $tmp .= '> ';
4313 print '<script type="text/javascript">
4314 jQuery(document).ready(function() {
4315 jQuery("#fac_replacement").change(function() {
4316 console.log("We change fac_replacement");
4317 jQuery("#radio_replacement").prop("checked", true);
4318 });
4319 });
4320 </script>';
4321 $text = $tmp.'<label for="radio_replacement">'.$langs->trans("InvoiceReplacementAsk").'</label>';
4322 $text .= '<select class="flat" name="fac_replacement" id="fac_replacement"';
4323 if (!$options || $invoice_predefined->id > 0) {
4324 $text .= ' disabled';
4325 }
4326 $text .= '>';
4327 if ($options) {
4328 $text .= '<option value="-1">&nbsp;</option>';
4329 $text .= $options;
4330 } else {
4331 $text .= '<option value="-1">'.$langs->trans("NoReplacableInvoice").'</option>';
4332 }
4333 $text .= '</select>';
4334 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc"), 1, 'help', '', 0, 3);
4335 print $desc;
4336 print '</div></div>'."\n";
4337 }
4338 } else {
4339 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
4340 print '<div class="listofinvoicetype"><div class="">';
4341 $tmp = '<input type="radio" name="type" id="radio_situation" value="0" disabled> ';
4342 $text = $tmp.'<label class="opacitymedium">'.$langs->trans("InvoiceSituationAsk").'</label> ';
4343 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceFirstSituationDesc").'<br><br>'.$langs->trans("YouMustCreateInvoiceFromThird"), 1, 'help', 'nowraponall', 0, 3, 'firstsituationonsmartphone');
4344 print $desc;
4345 print '</div></div>'."\n";
4346 }
4347
4348 print '<div class="listofinvoicetype"><div class="">';
4349 $tmp = '<input type="radio" name="type" id="radio_replacement" value="0" disabled> ';
4350 $text = $tmp.'<label for="radio_replacement" class="opacitymedium">'.$langs->trans("InvoiceReplacement").'</label> ';
4351 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc").'<br><br>'.$langs->trans("YouMustCreateInvoiceFromThird"), 1, 'help', 'nowraponall', 0, 3, 'replacementonsmartphone');
4352 print $desc;
4353 print '</div></div>'."\n";
4354 }
4355
4356 if (empty($origin)) {
4357 if ($socid > 0) {
4358 // Credit note
4359 if (!getDolGlobalString('INVOICE_DISABLE_CREDIT_NOTE')) {
4360 // Show link for credit note
4361 $facids = $facturestatic->list_qualified_avoir_invoices($soc->id);
4362 if ($facids < 0) {
4363 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
4364 exit;
4365 }
4366 $optionsav = "";
4367 $newinvoice_static = new Facture($db);
4368 foreach ($facids as $key => $valarray) {
4369 $newinvoice_static->id = $key;
4370 $newinvoice_static->ref = $valarray ['ref'];
4371 $newinvoice_static->statut = $valarray ['status'];
4372 $newinvoice_static->status = $valarray ['status'];
4373 $newinvoice_static->type = $valarray ['type'];
4374 $newinvoice_static->paye = $valarray ['paye'];
4375 $newinvoice_static->paid = $valarray ['paye'];
4376
4377 $optionsav .= '<option value="'.$key.'"';
4378 if ($key == GETPOST('fac_avoir')) {
4379 $optionsav .= ' selected';
4380
4381 // pre-filled extra fields with selected credit note
4382 $newinvoice_static->fetch_optionals($key);
4383 $object->array_options = $newinvoice_static->array_options;
4384 }
4385 $optionsav .= '>';
4386 $optionsav .= $newinvoice_static->ref;
4387 $optionsav .= ' ('.$newinvoice_static->getLibStatut(1, $valarray ['paymentornot']).')';
4388 $optionsav .= '</option>';
4389 }
4390
4391 print '<div class="listofinvoicetype"><div class="">';
4392 $tmp = '<input type="radio" id="radio_creditnote" name="type" value="2"'.(GETPOST('type') == 2 ? ' checked' : '');
4393 if ((!$optionsav && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) || $invoice_predefined->id > 0) {
4394 $tmp .= ' disabled';
4395 }
4396 $tmp .= '> ';
4397 // Show credit note options only if we checked credit note and disable standard invoice if "create credit note" button is pressed
4398 print '<script type="text/javascript">
4399 jQuery(document).ready(function() {
4400 if (jQuery("#radio_creditnote").is(":checked")) {
4401 jQuery("#radio_standard").prop("disabled", true);
4402 jQuery("#radio_deposit").prop("disabled", true);
4403 } else {
4404 jQuery("#radio_standard").prop("disabled", false);
4405 jQuery("#radio_deposit").prop("disabled", false);
4406 }
4407 if (! jQuery("#radio_creditnote").is(":checked")) {
4408 jQuery("#credit_note_options").hide();
4409 }
4410 jQuery("#radio_creditnote").click(function() {
4411 jQuery("#credit_note_options").show();
4412 });
4413 jQuery("#radio_standard, #radio_replacement, #radio_deposit, #radio_situation, #radio_situation_bis").click(function() {
4414 console.log("We click on a radio to close credit not options");
4415 jQuery("#credit_note_options").hide();
4416 });
4417 });
4418 </script>';
4419 $text = '<label>'.$tmp.$langs->transnoentities("InvoiceAvoirAsk").'</label> ';
4420 $text .= '<select class="flat valignmiddle minwidth200" name="fac_avoir" id="fac_avoir"';
4421 if (!$optionsav || $invoice_predefined->id > 0) {
4422 $text .= ' disabled';
4423 }
4424 $text .= '>';
4425 if ($optionsav) {
4426 $text .= '<option value="-1">'.$langs->trans("InvoiceAvoirAskCombo").'</option>';
4427 $text .= $optionsav;
4428 } else {
4429 $text .= '<option value="-1">'.$langs->trans("NoInvoiceToCorrect").'</option>';
4430 }
4431 $text .= '</select>';
4432 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3);
4433 print $desc;
4434
4435 print '<div id="credit_note_options" class="clearboth paddingtop marginbottomonly">';
4436 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>";
4437 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>";
4438 // Adding a checkbox: "Automatically consume the credit note to close the corrected invoice" is better to be into
4439 // the confirm popup when we validate the credit note
4440 print '</div>';
4441
4442 print '</div></div>'."\n";
4443 }
4444 } else {
4445 print '<div class="listofinvoicetype"><div class="">';
4446 if (!getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
4447 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="0" disabled> ';
4448 } else {
4449 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="2" > ';
4450 }
4451 $text = $tmp.'<label class="opacitymedium" for="radio_creditnote">'.$langs->trans("InvoiceAvoir").'</label> ';
4452 //$text .= '<span class="opacitymedium hideonsmartphone">('.$langs->trans("YouMustCreateInvoiceFromThird").')</span> ';
4453 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc").'<br><br>'.$langs->trans("CreateCreditNoteWhenClientInvoiceExists"), 1, 'help', '', 0, 3, 'creditnoteonsmartphone');
4454 print $desc;
4455 print '</div></div>'."\n";
4456 }
4457 }
4458
4459 // Template invoice
4460 /*
4461 print '<div class="listofinvoicetype"><div class="">';
4462 $tmp = '<input type="radio" name="type" id="radio_template" value="0" disabled> ';
4463 $text = $tmp.'<label class="opacitymedium" for="radio_template">'.$langs->trans("RepeatableInvoice").'</label> ';
4464 $desc = $form->textwithpicto($text, $langs->transnoentities("YouMustCreateStandardInvoiceFirstDesc"), 1, 'help', '', 0, 3, 'templateonsmartphone');
4465 print $desc;
4466 print '</div></div>'."\n";
4467 */
4468
4469 print '</div><br>';
4470
4471
4472 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) { // Hidden conf
4473 // Add auto select default document model
4475 $jsListType = '';
4476 foreach ($listtType as $type) {
4477 $thisTypeConfName = 'FACTURE_ADDON_PDF_'.$type;
4478 $current = getDolGlobalString($thisTypeConfName, getDolGlobalString('FACTURE_ADDON_PDF'));
4479 $jsListType .= (!empty($jsListType) ? ',' : '').'"'.$type.'":"'.$current.'"';
4480 }
4481
4482 print '<script type="text/javascript">
4483 $(document).ready(function() {
4484 var listType = {'.$jsListType.'};
4485 $("[name=\'type\']").change(function() {
4486 console.log("change name=type");
4487 if ($( this ).prop("checked"))
4488 {
4489 if(($( this ).val() in listType))
4490 {
4491 $("#model").val(listType[$( this ).val()]).trigger("change");
4492 }
4493 else
4494 {
4495 $("#model").val("' . getDolGlobalString('FACTURE_ADDON_PDF').'").trigger("change");
4496 }
4497 }
4498 });
4499 });
4500 </script>';
4501 }
4502
4503
4504 print '</td></tr>';
4505
4506 // Invoice Subtype
4507 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED')) {
4508 print '<tr><td class="fieldrequired">'.$langs->trans('InvoiceSubtype').'</td><td colspan="2">';
4509 print $form->getSelectInvoiceSubtype(GETPOSTINT('subtype'), 'subtype', 1, 0, '');
4510 print '</td></tr>';
4511 }
4512
4513 // Discounts for the known third party
4514 if ($socid > 0) {
4515 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td><td colspan="2">';
4516
4517 $thirdparty = $soc; // used by object_discounts.tpl.php
4518 $discount_type = 0; // used by object_discounts.tpl.php
4519 $backtopage = $_SERVER["PHP_SELF"].'?socid='.$thirdparty->id.'&action='.$action.'&origin='.urlencode((string) (GETPOST('origin'))).'&originid='.urlencode((string) (GETPOSTINT('originid')));
4520
4521 // loading object_discounts.tpl.php from module core/tpl if exists
4522 $defaulttpldir = '/core/tpl';
4523 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4524 foreach ($dirtpls as $module => $reldir) {
4525 $res = 0;
4526 if (!empty($module)) {
4527 $tpl = dol_buildpath($reldir.'/object_discounts.tpl.php');
4528 } else {
4529 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/object_discounts.tpl.php';
4530 }
4531 if (file_exists($tpl)) {
4532 if (empty($conf->file->strict_mode)) {
4533 $res = @include $tpl;
4534 } else {
4535 $res = include $tpl;
4536 }
4537 }
4538 if ($res) {
4539 break;
4540 }
4541 }
4542
4543 print '</td></tr>';
4544 }
4545
4546 $newdateinvoice = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
4547 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
4548
4549 // Date invoice
4550 print '<tr><td class="fieldrequired">'.$langs->trans('DateInvoice').'</td><td colspan="2">';
4551 print img_picto('', 'action', 'class="pictofixedwidth"');
4552 print $form->selectDate($newdateinvoice ? $newdateinvoice : $dateinvoice, '', 0, 0, 0, "add", 1, 1);
4553 print '</td></tr>';
4554
4555 // Date point of tax
4556 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
4557 print '<tr><td class="fieldrequired">'.$langs->trans('DatePointOfTax').'</td><td colspan="2">';
4558 print img_picto('', 'action', 'class="pictofixedwidth"');
4559 print $form->selectDate($date_pointoftax ? $date_pointoftax : -1, 'date_pointoftax', 0, 0, 0, "add", 1, 1);
4560 print '</td></tr>';
4561 }
4562
4563 // Payment term
4564 print '<tr><td class="nowrap fieldrequired">'.$langs->trans('PaymentConditionsShort').'</td><td colspan="2">';
4565 print img_picto('', 'payment', 'class="pictofixedwidth"');
4566 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', -1, 1, 0, 'maxwidth500 widthcentpercentminusx');
4567 print '</td></tr>';
4568
4569 // Retained warranty
4570 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
4571 $rwStyle = 'display:none;';
4572 if (in_array(GETPOSTINT('type'), $retainedWarrantyInvoiceAvailableType)) {
4573 $rwStyle = '';
4574 }
4575
4576 $retained_warranty = GETPOSTINT('retained_warranty');
4577 if (empty($retained_warranty)) {
4578 if ($objectsrc !== null && property_exists($objectsrc, 'retained_warranty') && !empty($objectsrc->retained_warranty)) { // use previous situation value
4579 // Facture->retained_warranty (does not exist on Expedition)
4580 $retained_warranty = $objectsrc->retained_warranty; // @phan-suppress-current-line PhanUndeclaredProperty
4581 }
4582 }
4583 $retained_warranty_js_default = !empty($retained_warranty) ? $retained_warranty : getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_PERCENT');
4584
4585 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('RetainedWarranty').'</td><td colspan="2">';
4586 print '<input id="new-situation-invoice-retained-warranty" name="retained_warranty" type="number" value="'.$retained_warranty.'" step="0.01" min="0" max="100" />%';
4587
4588 // Retained warranty payment term
4589 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('PaymentConditionsShortRetainedWarranty').'</td><td colspan="2">';
4590 $retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
4591 if (empty($retained_warranty_fk_cond_reglement)) {
4592 $retained_warranty_fk_cond_reglement = getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
4593 if ($objectsrc instanceOf Facture && !empty($objectsrc->retained_warranty_fk_cond_reglement)) { // use previous situation value
4594 // Facture->retained_warranty_fk_cond_reglement (does not exist on Expedition)
4595 $retained_warranty_fk_cond_reglement = $objectsrc->retained_warranty_fk_cond_reglement; // @phan-suppress-current-line PhanUndeclaredProperty
4596 }
4597 }
4598 print $form->getSelectConditionsPaiements($retained_warranty_fk_cond_reglement, 'retained_warranty_fk_cond_reglement', -1, 1);
4599 print '</td></tr>';
4600
4601 print '<script type="text/javascript">
4602 $(document).ready(function() {
4603 $("[name=\'type\']").change(function() {
4604 if($( this ).prop("checked") && $.inArray($( this ).val(), '.json_encode($retainedWarrantyInvoiceAvailableType).' ) !== -1)
4605 {
4606 $(".retained-warranty-line").show();
4607 $("#new-situation-invoice-retained-warranty").val("'.(float) $retained_warranty_js_default.'");
4608 }
4609 else{
4610 $(".retained-warranty-line").hide();
4611 $("#new-situation-invoice-retained-warranty").val("");
4612 }
4613 });
4614
4615 $("[name=\'type\']:checked").trigger("change");
4616 });
4617 </script>';
4618 }
4619
4620 // Payment mode
4621 print '<tr><td>'.$langs->trans('PaymentMode').'</td><td colspan="2">';
4622 print img_picto('', 'bank', 'class="pictofixedwidth"');
4623 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth250 widthcentpercentminusx', 1);
4624 print '</td></tr>';
4625
4626 // Bank Account
4627 if (isModEnabled("bank")) {
4628 print '<tr><td>'.$langs->trans('DefaultBankAccount').'</td><td colspan="2">';
4629 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4630 print $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth250 widthcentpercentminusx', 1);
4631 print '</td></tr>';
4632 }
4633
4634 // Source / Channel - What trigger creation
4635 if (!getDolGlobalInt('INVOICE_DISABLE_SOURCE')) {
4636 print '<tr><td>'.$langs->trans('Source').'</td><td>';
4637 print img_picto('', 'question', 'class="pictofixedwidth"');
4638 $form->selectInputReason((string) $inputReasonId, 'input_reason_id', '', 1, 'maxwidth250 widthcentpercentminusx');
4639 print '</td></tr>';
4640 } else {
4641 print '<input type="hidden" name="input_reason_id" value="'.((string) $inputReasonId).'">';
4642 }
4643
4644 // Project
4645 if (isModEnabled('project') && is_object($formproject)) {
4646 $langs->load('projects');
4647 print '<tr><td>'.$langs->trans('Project').'</td><td colspan="2">';
4648 print img_picto('', 'project', 'class="pictofixedwidth"');
4649
4650 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');
4651 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>';
4652 print '</td></tr>';
4653 }
4654
4655 // Incoterms
4656 if (isModEnabled('incoterm')) {
4657 print '<tr>';
4658 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), !empty($objectsrc->label_incoterms) ? $objectsrc->label_incoterms : '', 1).'</label></td>';
4659 print '<td colspan="2" class="maxwidthonsmartphone">';
4660 $incoterm_id = GETPOST('incoterm_id');
4661 $location_incoterms = GETPOST('location_incoterms');
4662 if (empty($incoterm_id)) {
4663 $incoterm_id = (!empty($objectsrc->fk_incoterms) ? $objectsrc->fk_incoterms : $soc->fk_incoterms);
4664 $location_incoterms = (!empty($objectsrc->location_incoterms) ? $objectsrc->location_incoterms : $soc->location_incoterms);
4665 }
4666 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
4667 print $form->select_incoterms($incoterm_id, $location_incoterms);
4668 print '</td></tr>';
4669 }
4670
4671 // Dispute
4672 /* Not necessary on creation
4673 print '<tr><td class="nowrap fieldrequired">'.$langs->trans('Dispute').'</td><td colspan="2">';
4674 //print yn($object->dispute_status);
4675 print '</td></tr>';
4676 */
4677
4678 // Category
4679 if (isModEnabled('category')) {
4680 // Categories
4681 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
4682 print $form->selectCategories(Categorie::TYPE_INVOICE, 'categories', $object);
4683 print "</td></tr>";
4684 }
4685
4686 // Other attributes
4687 $parameters = array('objectsrc' => !empty($objectsrc) ? $objectsrc : 0, 'colspan' => ' colspan="2"', 'cols' => '2', 'socid' => $socid);
4688 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
4689 print $hookmanager->resPrint;
4690 if (empty($reshook)) {
4691 if (getDolGlobalString('THIRDPARTY_PROPAGATE_EXTRAFIELDS_TO_INVOICE') && !empty($soc->id)) {
4692 // copy from thirdparty
4693 $tpExtrafields = new ExtraFields($db);
4694 $tpExtrafieldLabels = $tpExtrafields->fetch_name_optionals_label($soc->table_element);
4695 if ($soc->fetch_optionals() > 0) {
4696 $object->array_options = array_merge($object->array_options, $soc->array_options);
4697 }
4698 }
4699
4700 print $object->showOptionals($extrafields, 'create', $parameters);
4701 }
4702
4703 // Template to use by default
4704 include_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
4706 if (is_array($list) && count($list) > 0) {
4707 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) {
4708 $type = GETPOSTISSET('type') ? GETPOSTINT('type') : $object->type;
4709 // Hidden conf
4710 $paramkey = 'FACTURE_ADDON_PDF_'.$type;
4711 $preselected = getDolGlobalString($paramkey, getDolGlobalString('FACTURE_ADDON_PDF'));
4712 } else {
4713 $preselected = getDolGlobalString('FACTURE_ADDON_PDF');
4714 }
4715 if (count($list) > 1) {
4716 print '<tr><td>'.$langs->trans('Model').'</td>';
4717 print '<td colspan="2">';
4718 print img_picto('', 'pdf', 'class="pictofixedwidth"');
4719 print $form->selectarray('model', $list, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
4720 print "</td></tr>";
4721 } else {
4722 print '<input type="hidden" name="model" value="'.$preselected.'">';
4723 }
4724 }
4725
4726 // Multicurrency
4727 if (isModEnabled('multicurrency')) {
4728 print '<tr>';
4729 print '<td>'.$form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0).'</td>';
4730 print '<td colspan="2" class="maxwidthonsmartphone">';
4731 print img_picto('', 'currency', 'class="pictofixedwidth"');
4732 print $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
4733 print '</td></tr>';
4734 }
4735
4736 // Help of substitution key
4737 $htmltext = '';
4738 if (GETPOSTINT('fac_rec') > 0) {
4739 $dateexample = ($newdateinvoice ? $newdateinvoice : $dateinvoice);
4740 if (empty($dateexample)) {
4741 $dateexample = dol_now();
4742 }
4743 $substitutionarray = array(
4744 '__TOTAL_HT__' => $langs->trans("AmountHT").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ht).')',
4745 '__TOTAL_TTC__' => $langs->trans("AmountTTC").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ttc).')',
4746 '__INVOICE_PREVIOUS_MONTH__' => $langs->trans("PreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%m').')',
4747 '__INVOICE_MONTH__' => $langs->trans("MonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%m').')',
4748 '__INVOICE_NEXT_MONTH__' => $langs->trans("NextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%m').')',
4749 '__INVOICE_PREVIOUS_MONTH_TEXT__' => $langs->trans("TextPreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%B').')',
4750 '__INVOICE_MONTH_TEXT__' => $langs->trans("TextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%B').')',
4751 '__INVOICE_NEXT_MONTH_TEXT__' => $langs->trans("TextNextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%B').')',
4752 '__INVOICE_PREVIOUS_YEAR__' => $langs->trans("PreviousYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'y'), '%Y').')',
4753 '__INVOICE_YEAR__' => $langs->trans("YearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%Y').')',
4754 '__INVOICE_NEXT_YEAR__' => $langs->trans("NextYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'y'), '%Y').')'
4755 );
4756
4757 $htmltext = '<i>'.$langs->trans("FollowingConstantsWillBeSubstituted").':<br>';
4758 foreach ($substitutionarray as $key => $val) {
4759 $htmltext .= $key.' = '.$langs->trans($val).'<br>';
4760 }
4761 $htmltext .= '</i>';
4762 }
4763
4764 // Public note
4765 print '<tr>';
4766 print '<td class="tdtop">';
4767 print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
4768 print '</td>';
4769 print '<td valign="top" colspan="2">';
4770 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
4771 print $doleditor->Create(1);
4772
4773 // Private note
4774 if (empty($user->socid)) {
4775 print '<tr>';
4776 print '<td class="tdtop">';
4777 print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
4778 print '</td>';
4779 print '<td valign="top" colspan="2">';
4780 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
4781 print $doleditor->Create(1);
4782 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
4783 print '</td></tr>';
4784 }
4785
4786 // Lines from source (TODO Show them also when creating invoice from template invoice)
4787 if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($classname)) {
4788 $langs->loadLangs(array('orders', 'propal'));
4789
4790 // TODO for compatibility
4791 if ($origin == 'contrat') {
4792 '@phan-var-force Contrat $objectsrc';
4793 // Recalculate contrat->price (excl. VAT), contrat->total (incl. VAT), contrat->tva
4794 $objectsrc->update_price(1, 'auto', 1);
4795 }
4796
4797 print "\n<!-- Show ref of origin ".$classname." -->\n";
4798 print '<input type="hidden" name="amount" value="'.$objectsrc->total_ht.'">'."\n";
4799 print '<input type="hidden" name="total" value="'.$objectsrc->total_ttc.'">'."\n";
4800 print '<input type="hidden" name="tva" value="'.$objectsrc->total_tva.'">'."\n";
4801 // The commented lines below are fields already added as hidden parameters before
4802 //print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
4803 //print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
4804
4805 switch (get_class($objectsrc)) {
4806 case 'Propal':
4807 $newclassname = 'CommercialProposal';
4808 break;
4809 case 'Commande':
4810 $newclassname = 'Order';
4811 break;
4812 case 'Expedition':
4813 $newclassname = 'Sending';
4814 break;
4815 case 'Contrat':
4816 $newclassname = 'Contract';
4817 break;
4818 case 'Fichinter':
4819 $newclassname = 'Intervention';
4820 break;
4821 default:
4822 $newclassname = get_class($objectsrc);
4823 }
4824
4825 // Ref of origin
4826 print '<tr><td>'.$langs->trans($newclassname).'</td>';
4827 print '<td colspan="2">';
4828 print $objectsrc->getNomUrl(1);
4829 // We check if Origin document (id and type is known) has already at least one invoice attached to it
4830 $objectsrc->fetchObjectLinked($originid, $origin, null, 'facture');
4831 if (isset($objectsrc->linkedObjects['facture']) && is_array($objectsrc->linkedObjects['facture']) && count($objectsrc->linkedObjects['facture']) >= 1) {
4832 setEventMessages('WarningBillExist', null, 'warnings');
4833 echo ' - '.$langs->trans('LatestRelatedBill').' '.end($objectsrc->linkedObjects['facture'])->getNomUrl(1);
4834 }
4835 echo '</td></tr>';
4836
4837 print '<tr><td>'.$langs->trans('AmountHT').'</td><td colspan="2">'.price($objectsrc->total_ht, 1, $langs, 1, -1, '', $conf->currency).'</td></tr>';
4838 print '<tr><td>'.$langs->trans('AmountVAT').'</td><td colspan="2">'.price($objectsrc->total_tva, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4839 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
4840 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>";
4841 }
4842
4843 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
4844 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>";
4845 }
4846 print '<tr><td>'.$langs->trans('AmountTTC').'</td><td colspan="2">'.price($objectsrc->total_ttc, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
4847
4848 if (isModEnabled('multicurrency') && $objectsrc->multicurrency_code != $conf->currency) {
4849 //var_dump($objectsrc);
4850 print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ht, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code).'</td></tr>';
4851 print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_tva, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
4852 print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ttc, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
4853 }
4854 }
4855
4856 print "</table>\n";
4857 }
4858 print dol_get_fiche_end();
4859
4860 print $form->buttonsSaveCancel("CreateDraft");
4861
4862 // Show origin lines
4863 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
4864 print '<br>';
4865
4866 $title = $langs->trans('ProductsAndServices');
4867 print load_fiche_titre($title);
4868
4869 print '<div class="div-table-responsive-no-min">';
4870 print '<table class="noborder centpercent">';
4871
4872 $objectsrc->printOriginLinesList('', $selectedLines);
4873
4874 print '</table>';
4875 print '</div>';
4876 }
4877
4878 print "</form>\n";
4879} elseif ($id > 0 || !empty($ref)) {
4880 if (empty($object->id)) {
4881 $langs->load('errors');
4882 echo '<div class="error">'.$langs->trans("ErrorRecordNotFound").'</div>';
4883 llxFooter();
4884 exit;
4885 }
4886
4887 /*
4888 * Show object in view mode
4889 */
4890
4891 if ($user->socid > 0 && $user->socid != $object->socid) {
4892 accessforbidden('', 0, 1);
4893 }
4894
4895 $result = $object->fetch_thirdparty();
4896
4897 $result = $soc->fetch($object->socid);
4898 if ($result < 0) {
4900 }
4901 $selleruserevenustamp = $mysoc->useRevenueStamp();
4902
4903 $totalpaid = $object->getSommePaiement();
4904 $totalcreditnotes = $object->getSumCreditNotesUsed();
4905 $totaldeposits = $object->getSumDepositsUsed();
4906 //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits."
4907 // selleruserrevenuestamp=".$selleruserevenustamp;
4908
4909 // We can also use bcadd to avoid pb with floating points
4910 // For example print 239.2 - 229.3 - 9.9; does not return 0.
4911 $resteapayer = price2num($object->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
4912
4913 // Multicurrency
4914 if (isModEnabled('multicurrency')) {
4915 $multicurrency_totalpaid = $object->getSommePaiement(1);
4916 $multicurrency_totalcreditnotes = $object->getSumCreditNotesUsed(1);
4917 $multicurrency_totaldeposits = $object->getSumDepositsUsed(1);
4918 $multicurrency_resteapayer = price2num($object->multicurrency_total_ttc - $multicurrency_totalpaid - $multicurrency_totalcreditnotes - $multicurrency_totaldeposits, 'MT');
4919 // Code to fix case of corrupted data
4920 // 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
4921 // 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)
4922 if ($resteapayer == 0 && $multicurrency_resteapayer != 0 && $object->multicurrency_code != $conf->currency) {
4923 $resteapayer = price2num((float) $multicurrency_resteapayer / $object->multicurrency_tx, 'MT');
4924 }
4925 }
4926
4927 if ($object->paye || $object->status == $object::STATUS_CLOSED) {
4928 $resteapayer = 0;
4929 }
4930 $resteapayeraffiche = $resteapayer;
4931
4932 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) { // Never use this
4933 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
4934 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
4935 } else {
4936 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
4937 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
4938 }
4939
4940 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
4941 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
4942 $absolute_discount = price2num($absolute_discount, 'MT');
4943 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
4944
4945 $nb_creditnote_notyetavailable = $soc->getOpenCreditNotesNotYetConvertedIntoDiscount(0);
4946
4947 $author = new User($db);
4948 if ($object->user_creation_id) {
4949 $author->fetch($object->user_creation_id);
4950 }
4951
4952 $objectidnext = $object->getIdReplacingInvoice();
4953
4954 $head = facture_prepare_head($object);
4955
4956 print dol_get_fiche_head($head, 'compta', $langs->trans('InvoiceCustomer'), -1, $object->picto, 0, '', '', 0, '', 1);
4957
4958 $formconfirm = '';
4959
4960 // Confirmation of the conversion of the credit into a reduction
4961 if ($action == 'converttoreduc') {
4963 $type_fac = 'ExcessReceived';
4964 } elseif ($object->type == Facture::TYPE_CREDIT_NOTE) {
4965 $type_fac = 'CreditNote';
4966 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
4967 $type_fac = 'Deposit';
4968 } else {
4969 $type_fac = '';
4970 }
4971 $text = $langs->trans('ConfirmConvertToReduc', strtolower($langs->transnoentities($type_fac)));
4972 $text .= '<br>'.$langs->trans('ConfirmConvertToReduc2');
4973 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('ConvertToReduc'), $text, 'confirm_converttoreduc', '', "yes", 2);
4974 }
4975
4976 // Confirmation to delete invoice
4977 if ($action == 'delete') {
4978 $text = $langs->trans('ConfirmDeleteBill', $object->ref);
4979 $formquestion = array();
4980
4981 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL') && $object->status >= 1) {
4982 $qualified_for_stock_change = 0;
4983 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
4984 $qualified_for_stock_change = $object->hasProductsOrServices(2);
4985 } else {
4986 $qualified_for_stock_change = $object->hasProductsOrServices(1);
4987 }
4988
4989 if ($qualified_for_stock_change) {
4990 $langs->load("stocks");
4991 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
4992 $formproduct = new FormProduct($db);
4993 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
4994 $forcecombo = 0;
4995 if ($conf->browser->name == 'ie') {
4996 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
4997 }
4998 $formquestion = array(
4999 // 'text' => $langs->trans("ConfirmClone"),
5000 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
5001 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
5002 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1, 0, 0, $langs->trans("NoStockAction"), 0, $forcecombo))
5003 );
5004 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', $formquestion, "yes", 1);
5005 } else {
5006 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
5007 }
5008 } else {
5009 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
5010 }
5011 }
5012
5013 // Confirmation to remove invoice from cycle
5014 if ($action == 'situationout') {
5015 $text = $langs->trans('ConfirmRemoveSituationFromCycle', $object->ref);
5016 $label = $langs->trans("ConfirmOuting");
5017 $formquestion = array();
5018 // remove situation from cycle
5019 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
5020 && $usercancreate
5021 && !$objectidnext
5022 && $object->is_last_in_cycle()
5023 && $usercanunvalidate
5024 ) {
5025 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $label, $text, 'confirm_situationout', $formquestion, "yes", 1);
5026 }
5027 }
5028
5029 // Confirmation of validation
5030 if ($action == 'valid') {
5031 // we check object has a draft number
5032 $objectref = substr($object->ref, 1, 4);
5033 if ($objectref == 'PROV') {
5034 //$savdate = $object->date;
5035 if (getDolGlobalString('FAC_FORCE_DATE_VALIDATION')) {
5036 $object->date = dol_now();
5037 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
5038 }
5039 $numref = $object->getNextNumRef($soc);
5040 // $object->date=$savdate;
5041 } else {
5042 $numref = (string) $object->ref;
5043 }
5044
5045 $text = $langs->trans('ConfirmValidateBill', $numref);
5046 if (getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
5047 $text .= '<br><br>';
5048 $text .= img_picto('', 'warning', 'class="pictofixedwidth"').' '.$langs->trans('WarningInvoiceCanNeverBeEdited');
5049 }
5050 if (isModEnabled('notification')) {
5051 require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
5052 $notify = new Notify($db);
5053 $text .= '<br>';
5054 $text .= $notify->confirmMessage('BILL_VALIDATE', $object->socid, $object);
5055 }
5056 $formquestion = array();
5057
5058 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
5059 $qualified_for_stock_change = 0;
5060 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
5061 $qualified_for_stock_change = $object->hasProductsOrServices(2);
5062 } else {
5063 $qualified_for_stock_change = $object->hasProductsOrServices(1);
5064 }
5065
5066 if ($qualified_for_stock_change) {
5067 $langs->load("stocks");
5068 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
5069 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
5070 $formproduct = new FormProduct($db);
5071 $warehouse = new Entrepot($db);
5072 $warehouse_array = $warehouse->list_array();
5073 if (count($warehouse_array) == 1) {
5074 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockIncrease", current($warehouse_array)) : $langs->trans("WarehouseForStockDecrease", current($warehouse_array));
5075 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
5076 } else {
5077 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockIncrease") : $langs->trans("SelectWarehouseForStockDecrease");
5078 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
5079 }
5080 $formquestion = array(
5081 // 'text' => $langs->trans("ConfirmClone"),
5082 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
5083 // 1),
5084 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
5085 // => 1),
5086 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
5087 }
5088 }
5089 if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) { // Can happen only if getDolGlobalString('FACTURE_ENABLE_NEGATIVE') is on
5090 $text .= '<br>'.img_warning().' '.$langs->trans("ErrorInvoiceOfThisTypeMustBePositive");
5091 }
5092
5093 // mandatoryPeriod
5094 $nbMandated = 0;
5095 foreach ($object->lines as $line) {
5096 $res = $line->fetch_product();
5097 if ($res > 0) {
5098 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
5099 $nbMandated++;
5100 break;
5101 }
5102 }
5103 }
5104 if ($nbMandated > 0) {
5105 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
5106 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
5107 $error++;
5108 } else {
5109 $text .= '<div><span class="clearboth nowraponall warning">'.img_warning().$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
5110 }
5111 }
5112
5113 if (!$error) {
5114 $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);
5115 }
5116 }
5117
5118 // Confirm back to draft status (action = 'modif')
5119 if ($action == 'modif') {
5120 $oktomodif = 1; // Assume we can modify by default
5121
5122 $testvalue = $object->isEditable();
5123 if ($testvalue < 0) {
5124 switch ($testvalue) {
5125 case -1:
5126 // Dispatched in bookkeeping
5127 setEventMessages($langs->trans("DisabledBecauseDispatchedInBookkeeping"), null, 'errors');
5128 break;
5129 case -2:
5130 // Not last invoice
5131 setEventMessages($langs->trans("DisabledBecauseNotLastInvoice"), null, 'errors');
5132 break;
5133 case -3:
5134 // Not last situation invoice
5135 setEventMessages($langs->trans("DisabledBecauseNotLastSituationInvoice"), null, 'errors');
5136 break;
5137 case -4:
5138 // At least one payment made
5139 setEventMessages($langs->trans("DisabledBecauseThereIsAPayment"), null, 'errors');
5140 break;
5141 case -5:
5142 // Already sent by email
5143 setEventMessages($langs->trans("DisabledBecauseAlreadySentByEmail"), null, 'errors');
5144 break;
5145 case -6:
5146 // Already printed once
5147 setEventMessages($langs->trans("DisabledBecauseAlreadyPrintedOnce"), null, 'errors');
5148 break;
5149 case -7:
5150 // Already validated
5151 setEventMessages($langs->trans("DisabledBecauseVersionProtected").(empty($object->error) ? '' : ': '.$object->error), null, 'errors');
5152 break;
5153 default:
5154 // Other error
5155 setEventMessages($langs->trans("DisabledBecauseNotEditable").(empty($object->error) ? ': UnknownReason' : ': '.$object->error), null, 'errors');
5156 break;
5157 }
5158 $oktomodif = 0;
5159 $action = '';
5160 }
5161
5162 if ($oktomodif) {
5163 $text = $langs->trans('ConfirmUnvalidateBill', $object->ref);
5164 $formquestion = array();
5165
5166 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
5167 $qualified_for_stock_change = 0;
5168 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
5169 $qualified_for_stock_change = $object->hasProductsOrServices(2);
5170 } else {
5171 $qualified_for_stock_change = $object->hasProductsOrServices(1);
5172 }
5173
5174 if ($qualified_for_stock_change) {
5175 $langs->load("stocks");
5176 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
5177 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
5178 $formproduct = new FormProduct($db);
5179 $warehouse = new Entrepot($db);
5180 $warehouse_array = $warehouse->list_array();
5181 if (count($warehouse_array) == 1) {
5182 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockDecrease", current($warehouse_array)) : $langs->trans("WarehouseForStockIncrease", current($warehouse_array));
5183 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
5184 } else {
5185 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
5186 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
5187 }
5188 $formquestion = array(
5189 // 'text' => $langs->trans("ConfirmClone"),
5190 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
5191 // 1),
5192 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
5193 // => 1),
5194 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
5195 }
5196 }
5197
5198 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('UnvalidateBill'), $text, 'confirm_modif', $formquestion, "yes", 1);
5199 }
5200 }
5201
5202 // Confirmation of status abandoned (when no payment never done)
5203 if ($action == 'canceled') {
5204 // If there is a replacement invoice not yet validated (draft state),
5205 // it is not allowed to classify the invoice as abandoned.
5206
5207 $statusreplacement = 0;
5208
5209 if ($objectidnext) {
5210 $facturereplacement = new Facture($db);
5211 $facturereplacement->fetch($objectidnext);
5212 $statusreplacement = $facturereplacement->status;
5213 }
5214 if ($objectidnext && $statusreplacement == 0) {
5215 print '<div class="error">'.$langs->trans("ErrorCantCancelIfReplacementInvoiceNotValidated").'</div>';
5216 } else {
5217 $close = array();
5218 // Code
5219 $close[1]['code'] = 'badcustomer';
5220 $close[2]['code'] = 'abandon';
5221 // Help
5222 $close[1]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
5223 $close[2]['label'] = $langs->trans("ConfirmClassifyAbandonReasonOtherDesc");
5224 // Text
5225 $close[1]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $object->ref), $close[1]['label'], 1);
5226 $close[2]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyAbandonReasonOther"), $close[2]['label'], 1);
5227 // arrayreasons
5228 $arrayreasons = [];
5229 $arrayreasons[$close[1]['code']] = $close[1]['reason'];
5230 $arrayreasons[$close[2]['code']] = $close[2]['reason'];
5231
5232 // Create a form table
5233 $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'));
5234
5235 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('CancelBill'), $langs->trans('ConfirmCancelBill', $object->ref), 'confirm_canceled', $formquestion, "yes", 1, 300);
5236 }
5237 }
5238
5239 // Confirmation of payment classification (when some payment started)
5240 if ($action == 'paid' && ($resteapayer <= 0 || (getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') && $resteapayer == $object->total_ttc))) {
5241 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidBill', $object->ref), 'confirm_paid', '', "yes", 1);
5242 }
5243 if ($action == 'paid' && $resteapayer > 0 && (!getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') || $resteapayer != $object->total_ttc)) {
5244 $close = array();
5245 // Code
5246 $i = 0;
5247 $close[$i]['code'] = $object::CLOSECODE_DISCOUNTVAT; // escompte
5248 $i++;
5249 $close[$i]['code'] = $object::CLOSECODE_BADDEBT;
5250 $i++;
5251 $close[$i]['code'] = $object::CLOSECODE_BANKCHARGE;
5252 $i++;
5253 $close[$i]['code'] = $object::CLOSECODE_WITHHOLDINGTAX;
5254 $i++;
5255 $close[$i]['code'] = $object::CLOSECODE_OTHER;
5256 $i++;
5257 // Help
5258 $i = 0;
5259 $close[$i]['label'] = $langs->trans("HelpEscompte").'<br><br>'.$langs->trans("ConfirmClassifyPaidPartiallyReasonDiscountVatDesc");
5260 $i++;
5261 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
5262 $i++;
5263 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBankChargeDesc");
5264 $i++;
5265 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonWithholdingTaxDesc");
5266 $i++;
5267 $close[$i]['label'] = $langs->trans("Other");
5268 $i++;
5269 // Texte
5270 $i = 0;
5271 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonDiscount", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5272 $i++;
5273 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5274 $i++;
5275 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBankCharge", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
5276 $i++;
5277 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonWithholdingTax"), $close[$i]['label'], 1);
5278 $i++;
5279 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("Other"), $close[$i]['label'], 1);
5280 $i++;
5281 // arrayreasons[code]=reason
5282 $arrayreasons = [];
5283 foreach ($close as $key => $val) {
5284 $arrayreasons[$close[$key]['code']] = '<span class="small">'.$close[$key]['reason'].'</span>';
5285 }
5286
5287 // Create a form table
5288 $formquestion = array(
5289 'text' => $langs->trans("ConfirmClassifyPaidPartiallyQuestion"),
5290 0 => array('type' => 'radio', 'name' => 'close_code', 'label' => '', 'values' => $arrayreasons),
5291 1 => array('type' => 'text', 'name' => 'close_note', 'moreattr' => 'placeholder = "'.$langs->trans("Comment").'"', 'value' => '', 'morecss' => 'minwidth300'),
5292 2 => array('type' => 'separator')
5293 );
5294 // Incomplete payment. We ask if reason = discount or other
5295 $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);
5296 }
5297
5298 if ($action == 'deletepayment') {
5299 $payment_id = GETPOST('paiement_id');
5300 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&paiement_id='.$payment_id, $langs->trans('DeletePayment'), $langs->trans('ConfirmDeletePayment'), 'confirm_delete_paiement', '', 'no', 1);
5301 }
5302
5303 // Confirm deletion of product line
5304 if ($action == 'ask_deleteline') {
5305 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 'no', 1);
5306 }
5307
5308 // Confirm deletion of subtotal line
5309 if ($action == 'ask_subtotal_deleteline') {
5310 $langs->load("subtotals");
5311 $title = "DeleteSubtotalLine";
5312 $question = "ConfirmDeleteSubtotalLine";
5313 if (GETPOST('type') == 'title') {
5314 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
5315 $title = "DeleteTitleLine";
5316 $question = "ConfirmDeleteTitleLine";
5317 }
5318 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&lineid='.$lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
5319 }
5320
5321 // Clone confirmation
5322 if ($action == 'clone') {
5323 $filter = '(s.client:IN:1,2,3)';
5324 // Create an array for form
5325 $formquestion = array(
5326 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company($object->socid, 'socid', $filter, 1)),
5327 array('type' => 'date', 'name' => 'newdate', 'label' => $langs->trans("Date"), 'value' => dol_now())
5328 );
5329 // Request confirmation to clone
5330 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneInvoice', $object->ref), 'confirm_clone', $formquestion, 'yes', 1, 250);
5331 }
5332
5333 // Subtotal line form
5334 if ($action == 'add_title_line') {
5335 $langs->load('subtotals');
5336 $type = 'title';
5337 $depth_array = $object->getPossibleLevels($langs);
5338 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
5339 } elseif ($action == 'add_subtotal_line') {
5340 $langs->load('subtotals');
5341 $type = 'subtotal';
5342 $titles = $object->getPossibleTitles();
5343 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
5344 }
5345
5346 if ($action == "remove_file_comfirm") {
5347 $file = GETPOST('file', 'alpha');
5348
5349 $formconfirm = $form->formconfirm(
5350 $_SERVER["PHP_SELF"].'?facid='.$object->id.'&file='.urlencode($file),
5351 $langs->trans('DeleteFileHeader'),
5352 $langs->trans('DeleteFileText')."<br><br>".$file,
5353 'remove_file',
5354 '',
5355 'no',
5356 1
5357 );
5358 }
5359
5360 // Call Hook formConfirm
5361 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid, 'remainingtopay' => &$resteapayer);
5362 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
5363 if (empty($reshook)) {
5364 $formconfirm .= $hookmanager->resPrint;
5365 } elseif ($reshook > 0) {
5366 $formconfirm = $hookmanager->resPrint;
5367 }
5368
5369 // Print form confirm
5370 print $formconfirm;
5371
5372 // Invoice content
5373
5374 $linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
5375
5376 $morehtmlref = '<div class="refidno">';
5377 // Ref invoice
5378 if ($object->status == $object::STATUS_DRAFT && !$mysoc->isInEEC() && getDolGlobalString('INVOICE_ALLOW_FREE_REF')) {
5379 $morehtmlref .= $form->editfieldkey("Ref", 'ref', $object->ref, $object, (int) $usercancreate, 'string', '', 0, 1);
5380 $morehtmlref .= $form->editfieldval("Ref", 'ref', $object->ref, $object, (int) $usercancreate, 'string', '', null, null, '', 1);
5381 $morehtmlref .= '<br>';
5382 }
5383 // Ref customer
5384 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_customer, $object, (int) $usercancreate, 'string', '', 0, 1);
5385 $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);
5386 // Thirdparty
5387 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1, 'customer');
5388 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
5389 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$object->thirdparty->id.'">'.$langs->trans("OtherBills").'</a>)';
5390 }
5391 // Project
5392 if (isModEnabled('project')) {
5393 $langs->load("projects");
5394 $morehtmlref .= '<br>';
5395 if ($usercancreate) {
5396 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
5397 if ($action != 'classify') {
5398 $morehtmlref .= '<a class="editfielda" href="'.dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true).'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
5399 }
5400 $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');
5401 } else {
5402 if (!empty($object->fk_project)) {
5403 $proj = new Project($db);
5404 $proj->fetch($object->fk_project);
5405 $morehtmlref .= $proj->getNomUrl(1);
5406 if ($proj->title) {
5407 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
5408 }
5409 }
5410 }
5411 }
5412 $morehtmlref .= '</div>';
5413
5414 $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status
5415 $object->totalcreditnotes = $totalcreditnotes;
5416 $object->totaldeposits = $totaldeposits;
5417 $object->remaintopay = price2num($object->total_ttc - $object->totalpaid - $object->totalcreditnotes - $object->totaldeposits, 'MT');
5418
5419 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref, '', 0, '', '');
5420
5421 // Call Hook tabContentViewInvoice
5422 $parameters = array();
5423 // Note that $action and $object may be modified by hook
5424 $reshook = $hookmanager->executeHooks('tabContentViewInvoice', $parameters, $object, $action);
5425 if (empty($reshook)) {
5426 print '<div class="fichecenter">';
5427 print '<div class="fichehalfleft">';
5428 print '<div class="underbanner clearboth"></div>';
5429
5430 print '<table class="border centpercent tableforfield">';
5431
5432 // Type
5433 print '<tr><td class="fieldname_type">'.$langs->trans('Type').'</td><td class="valuefield fieldname_type">';
5434 print $object->getLibType(2);
5435 if ($object->subtype > 0) {
5436 print ' '.$object->getSubtypeLabel('facture');
5437 }
5438 if ($object->type == Facture::TYPE_REPLACEMENT) {
5439 $facreplaced = new Facture($db);
5440 $facreplaced->fetch($object->fk_facture_source);
5441 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("ReplaceInvoice", $facreplaced->getNomUrl(1, '', 32)).'</span>';
5442 }
5443 if ($object->type == Facture::TYPE_CREDIT_NOTE && !empty($object->fk_facture_source)) {
5444 $facusing = new Facture($db);
5445 $facusing->fetch($object->fk_facture_source);
5446 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("CorrectInvoice", $facusing->getNomUrl(1, '', 32)).'</span>';
5447 }
5448
5449 // Retrieve credit note ids (credit notes generated from this invoice)
5450 $object->getListIdAvoirFromInvoice();
5451
5452 if (!empty($object->creditnote_ids)) {
5453 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("InvoiceHasAvoir");
5454 $i = 0;
5455 foreach ($object->creditnote_ids as $invoiceid) {
5456 if ($i == 0) {
5457 print ' ';
5458 } else {
5459 print ',';
5460 }
5461 $creditnote = new Facture($db);
5462 $creditnote->fetch($invoiceid);
5463 print $creditnote->getNomUrl(1, '', 32);
5464 }
5465 print '</span>';
5466 }
5467 if ($objectidnext > 0) {
5468 $facthatreplace = new Facture($db);
5469 $facthatreplace->fetch($objectidnext);
5470 print ' <span class="opacitymediumbycolor paddingleft">'.str_replace('{s1}', $facthatreplace->getNomUrl(1), $langs->transnoentities("ReplacedByInvoice", '{s1}')).'</span>';
5471 }
5472
5474 $discount = new DiscountAbsolute($db);
5475 $result = $discount->fetch(0, $object->id);
5476 if ($result > 0) {
5477 print ' <span class="opacitymediumbycolor paddingleft">';
5478 $s = $langs->trans("CreditNoteConvertedIntoDiscount", '{s1}', '{s2}');
5479 $s = str_replace('{s1}', $object->getLibType(0), $s);
5480 $s = str_replace('{s2}', $discount->getNomUrl(1, 'discount'), $s);
5481 print $s;
5482 print '</span><br>';
5483 }
5484 }
5485
5486 if ($object->fk_fac_rec_source > 0) {
5487 $tmptemplate = new FactureRec($db);
5488 $result = $tmptemplate->fetch($object->fk_fac_rec_source);
5489 if ($result > 0) {
5490 print ' <span class="opacitymediumbycolor paddingleft">';
5491 $s = $langs->transnoentities("GeneratedFromTemplate", '{s1}');
5492 $s = str_replace('{s1}', $tmptemplate->getNomUrl(1, '', 32), $s);
5493 print $s;
5494 print '</span>';
5495 } else {
5496 print ' <span class="opacitymediumbycolor paddingleft">';
5497 print $langs->transnoentities("GeneratedFromTemplate", $langs->trans("ObjectDeleted"));
5498 print '</span>';
5499 }
5500 }
5501 print '</td></tr>';
5502
5503 // POS
5504 if (isModEnabled('takepos') || $object->module_source || getDolGlobalString('INVOICE_ALLOW_POS_SOURCE_EDIT')) {
5505 $langs->load("cashdesk");
5506 print '<tr><td class="fieldname_type">';
5507 print '<table class="nobordernopadding centpercent"><tr><td>';
5508 print $form->textwithpicto($langs->trans('PointOfSale'), $langs->trans('POSInfo'));
5509 print '</td>';
5510 if ($action != 'editposinfo' && $usercancreate) {
5511 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>';
5512 }
5513 print '</tr></table>';
5514 print '</td><td class="valuefield fieldname_type">';
5515 print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" name="formposinfo">';
5516 print '<input type="hidden" name="action" value="setposinfo">';
5517 print '<input type="hidden" name="token" value="' . newToken() . '">';
5518 if ($action == 'editposinfo') {
5519 print '<input type="text" class="maxwidth150" name="posmodule" placeholder="'.$langs->trans("POSModule").'" value="'.$object->module_source.'"> ';
5520 print '<input type="text" class="maxwidth100" name="posterminal" placeholder="'.$langs->trans("Terminal").'" value="'.$object->pos_source.'">';
5521 print '<input type="submit" class="button" name="submitposinfo" value="'.$langs->trans("Submit").'">';
5522 } else {
5523 if ($object->module_source) {
5524 print '<span class="opacitymediumbycolor paddingleft">'.dolPrintHTML(ucfirst($object->module_source).' - '.$langs->transnoentitiesnoconv("Terminal").' '.$object->pos_source).'</span>';
5525 }
5526 }
5527 print '</form>';
5528 print '</td></tr>';
5529 }
5530
5531 // Relative and absolute discounts
5532 print '<!-- Discounts -->'."\n";
5533 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td>';
5534 print '<td>';
5535 $thirdparty = $soc;
5536 $discount_type = 0;
5537 $backtopage = $_SERVER["PHP_SELF"].'?facid='.$object->id;
5538 $defaulttpldir = '/core/tpl';
5539 // loading object_discounts.tpl.php from module core/tpl if exists
5540 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5541 foreach ($dirtpls as $module => $reldir) {
5542 $res = 0;
5543 if (!empty($module)) {
5544 $tpl = dol_buildpath($reldir.'/object_discounts.tpl.php');
5545 } else {
5546 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/object_discounts.tpl.php';
5547 }
5548 if (file_exists($tpl)) {
5549 if (empty($conf->file->strict_mode)) {
5550 $res = @include $tpl;
5551 } else {
5552 $res = include $tpl;
5553 }
5554 }
5555 if ($res) {
5556 break;
5557 }
5558 }
5559
5560 print '</td></tr>';
5561
5562 // Date invoice
5563 print '<tr><td>';
5564 print '<table class="nobordernopadding centpercent"><tr><td>';
5565 print $langs->trans('DateInvoice');
5566 print '</td>';
5567 if ($action != 'editinvoicedate' && $object->status == $object::STATUS_DRAFT && $usercancreate && !getDolGlobalString('FAC_FORCE_DATE_VALIDATION')) {
5568 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>';
5569 }
5570 print '</tr></table>';
5571 print '</td><td>';
5572 if ($action == 'editinvoicedate') {
5573 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date, 'invoicedate');
5574 } else {
5575 print '<span class="valuedate">'.dol_print_date($object->date, 'day').'</span>';
5576 }
5577 print '</td>';
5578
5579 print '</tr>';
5580
5581 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
5582 // Date invoice point of tax (Leistungsdatum / service date for tax).
5583 // Only editable while the invoice is a draft -- once validated, the
5584 // invoice is a legally issued document and date_pointoftax is the
5585 // basis for the VAT-return period assignment under accrual taxation
5586 // (Soll-Versteuerung). To correct, set the invoice back to draft or
5587 // issue a credit note.
5588 $editable = ($usercancreate && $object->status < Facture::STATUS_VALIDATED);
5589 print '<tr><td>';
5590 print '<table class="nobordernopadding centpercent"><tr><td>';
5591 print $langs->trans('DatePointOfTax');
5592 print '</td>';
5593 if ($editable) {
5594 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>';
5595 }
5596 print '</tr></table>';
5597 print '</td><td>';
5598 if ($action == 'editdate_pointoftax' && $editable) {
5599 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date_pointoftax, 'date_pointoftax');
5600 } else {
5601 print '<span class="valuedate">'.dol_print_date($object->date_pointoftax, 'day').'</span>';
5602 }
5603 print '</td></tr>';
5604 }
5605
5606 // Source reason (why we have an invoice)
5607 if (!getDolGlobalInt('INVOICE_DISABLE_SOURCE')) {
5608 print '<tr><td>';
5609 print $form->editfieldkey('Source', 'input_reason', '', $object, (int) $usercancreate);
5610 print '</td><td class="valuefield">';
5611 if ($action == 'editinput_reason') {
5612 $form->formInputReason($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->demand_reason_id, 'input_reason_id', 1, 'maxwidth250 widthcentpercentminusx');
5613 } else {
5614 $form->formInputReason($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->demand_reason_id, 'none');
5615 }
5616 print '</td></tr>';
5617 }
5618
5619 // Payment term
5620 print '<tr><td>';
5621 print '<table class="nobordernopadding centpercent"><tr><td>';
5622 print $langs->trans('PaymentConditionsShort');
5623 print '</td>';
5624 if ($object->type != Facture::TYPE_CREDIT_NOTE && $action != 'editconditions' && $usercancreate) {
5625 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>';
5626 }
5627 print '</tr></table>';
5628 print '</td><td>';
5629 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
5630 if ($action == 'editconditions') {
5631 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->cond_reglement_id, 'cond_reglement_id');
5632 } else {
5633 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->cond_reglement_id, 'none');
5634 }
5635 }
5636 print '</td></tr>';
5637
5638 // Date payment term
5639 print '<tr><td>';
5640 print '<table class="nobordernopadding centpercent"><tr><td>';
5641 print $langs->trans('DateMaxPayment');
5642 print '</td>';
5643 if ($object->type != Facture::TYPE_CREDIT_NOTE && $action != 'editpaymentterm' && $usercancreate) {
5644 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>';
5645 }
5646 print '</tr></table>';
5647 print '</td><td>';
5648 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
5649 if ($action == 'editpaymentterm') {
5650 $form->form_date($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->date_lim_reglement, 'paymentterm');
5651 } else {
5652 print '<span class="valuedate">'.dol_print_date($object->date_lim_reglement, 'day').'</span>';
5653 if ($object->hasDelay()) {
5654 print img_warning($langs->trans('Late'));
5655 }
5656 }
5657 }
5658 print '</td></tr>';
5659
5660 // Payment mode
5661 print '<tr><td>';
5662 print '<table class="nobordernopadding centpercent"><tr><td>';
5663 print $langs->trans('PaymentMode');
5664 print '</td>';
5665 if ($action != 'editmode' && $usercancreate) {
5666 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>';
5667 }
5668 print '</tr></table>';
5669 print '</td><td>';
5670 if ($action == 'editmode') {
5671 $form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
5672 } else {
5673 $form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->mode_reglement_id, 'none', 'CRDT');
5674 }
5675 print '</td></tr>';
5676
5677 // Bank Account
5678 if (isModEnabled("bank")) {
5679 print '<tr><td class="nowrap">';
5680 print '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
5681 print $langs->trans('DefaultBankAccount');
5682 print '<td>';
5683 if (($action != 'editbankaccount') && $usercancreate) {
5684 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>';
5685 }
5686 print '</tr></table>';
5687 print '</td><td>';
5688 if ($action == 'editbankaccount') {
5689 $form->formSelectAccount($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->fk_account, 'fk_account', 1);
5690 } else {
5691 $form->formSelectAccount($_SERVER['PHP_SELF'].'?id='.$object->id, (string) $object->fk_account, 'none');
5692 }
5693 print "</td>";
5694 print '</tr>';
5695 }
5696
5697 // Incoterms
5698 if (isModEnabled('incoterm')) {
5699 print '<tr><td>';
5700 print '<table class="nobordernopadding centpercent"><tr><td>';
5701 print $langs->trans('IncotermLabel');
5702 print '<td><td class="right">';
5703 if ($usercancreate) {
5704 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=editincoterm&token='.newToken().'">'.img_edit().'</a>';
5705 }
5706 print '</td></tr></table>';
5707 print '</td>';
5708 print '<td>';
5709 if ($action != 'editincoterm') {
5710 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
5711 } else {
5712 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'].'?id='.$object->id);
5713 }
5714 print '</td></tr>';
5715 }
5716
5717 // Dispute open
5718 print '<tr><td>';
5719 print '<table class="nobordernopadding centpercent"><tr><td>';
5720 print $langs->trans('Dispute');
5721 print '<td><td class="right">';
5722 if ($usercancreate) {
5723 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=editdispute_status&token='.newToken().'">'.img_edit().'</a>';
5724 }
5725 print '</td></tr></table>';
5726 print '</td><td>';
5727 $liststatus = Facture::ARRAY_OF_DISPUTE_STATUS;
5728 if ($action != 'editdispute_status') {
5729 if ($object->dispute_status) {
5730 print $langs->trans($liststatus[$object->dispute_status]['label']);
5731 }
5732 } else {
5733 print '<form enctype="multipart/form-data" action="'.DOL_URL_ROOT.'/compta/facture/card.php" method="POST">';
5734 print '<input type="hidden" name="action" value="set_dispute_status">';
5735 print '<input type="hidden" name="token" value="'.newToken().'">';
5736 print '<input type="hidden" name="id" value="'.$object->id.'">';
5737 print '<input type="hidden" name="page_y" value="">';
5738 print $form->selectarray('dispute_status', $liststatus, $object->dispute_status, 0, 0, 0, '', 1);
5739 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Save").'">';
5740 print '</form>';
5741 }
5742 print '</td></tr>';
5743
5744 // Categories
5745 if (isModEnabled('category')) {
5746 print '<tr><td>';
5747 print '<table class="nobordernopadding centpercent"><tr><td>';
5748 print $langs->trans("Categories");
5749 print '<td><td class="right">';
5750 if ($usercancreate) {
5751 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
5752 } else {
5753 print '&nbsp;';
5754 }
5755 print '</td></tr></table>';
5756 print '</td>';
5757 print '<td>';
5758 if ($action == 'edittags') {
5759 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5760 print '<input type="hidden" name="action" value="settags">';
5761 print '<input type="hidden" name="token" value="'.newToken().'">';
5762 print $form->selectCategories(Categorie::TYPE_INVOICE, 'categories', $object);
5763 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5764 print '</form>';
5765 } else {
5766 print $form->showCategories($object->id, Categorie::TYPE_INVOICE, 1);
5767 }
5768 print "</td></tr>";
5769 }
5770
5771 $displayWarranty = false;
5772
5773 if (!empty($object->retained_warranty) || getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
5774 $displayWarranty = true;
5775 if (!in_array($object->type, $retainedWarrantyInvoiceAvailableType) && empty($object->retained_warranty)) {
5776 $displayWarranty = false;
5777 }
5778
5779 if ($displayWarranty) {
5780 // Retained Warranty
5781 print '<tr class="retained-warranty-lines" ><td>';
5782 print '<table id="retained-warranty-table" class="nobordernopadding centpercent"><tr><td>';
5783 print $langs->trans('RetainedWarranty');
5784 print '</td>';
5785 if ($action != 'editretainedwarranty' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5786 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>';
5787 }
5788
5789 print '</tr></table>';
5790 print '</td><td>';
5791 if ($action == 'editretainedwarranty' && $object->status == Facture::STATUS_DRAFT) {
5792 print '<form id="retained-warranty-form" method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5793 print '<input type="hidden" name="action" value="setretainedwarranty">';
5794 print '<input type="hidden" name="token" value="'.newToken().'">';
5795 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5796 print '<input name="retained_warranty" type="number" step="0.01" min="0" max="100" value="'.$object->retained_warranty.'" >';
5797 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5798 print '</form>';
5799 } else {
5800 print price($object->retained_warranty).'%';
5801 }
5802 print '</td></tr>';
5803
5804 // Retained warranty payment term
5805 print '<tr class="retained-warranty-lines" ><td>';
5806 print '<table id="retained-warranty-cond-reglement-table" class="nobordernopadding" width="100%"><tr><td>';
5807 print $langs->trans('PaymentConditionsShortRetainedWarranty');
5808 print '</td>';
5809 if ($action != 'editretainedwarrantypaymentterms' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5810 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>';
5811 }
5812
5813 print '</tr></table>';
5814 print '</td><td>';
5815 $defaultDate = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : strtotime('-1 years', $object->date_lim_reglement);
5816 if ($object->date > $defaultDate) {
5817 $defaultDate = $object->date;
5818 }
5819
5820 if ($action == 'editretainedwarrantypaymentterms' && $object->status == Facture::STATUS_DRAFT) {
5821 //date('Y-m-d',$object->date_lim_reglement)
5822 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5823 print '<input type="hidden" name="action" value="setretainedwarrantyconditions">';
5824 print '<input type="hidden" name="token" value="'.newToken().'">';
5825 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5826 $retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
5827 $retained_warranty_fk_cond_reglement = !empty($retained_warranty_fk_cond_reglement) ? $retained_warranty_fk_cond_reglement : $object->retained_warranty_fk_cond_reglement;
5828 $retained_warranty_fk_cond_reglement = !empty($retained_warranty_fk_cond_reglement) ? $retained_warranty_fk_cond_reglement : getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
5829 print $form->getSelectConditionsPaiements($retained_warranty_fk_cond_reglement, 'retained_warranty_fk_cond_reglement', -1, 1);
5830 print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
5831 print '</form>';
5832 } else {
5833 $form->form_conditions_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, (string) $object->retained_warranty_fk_cond_reglement, 'none');
5834 if (!$displayWarranty) {
5835 print img_picto($langs->trans('RetainedWarrantyNeed100Percent'), 'warning', 'class="pictowarning valignmiddle" ');
5836 }
5837 }
5838 print '</td></tr>';
5839
5840 // Retained Warranty payment date limit
5841 print '<tr class="retained-warranty-lines" ><td>';
5842 print '<table id="retained-warranty-date-limit-table" class="nobordernopadding" width="100%"><tr><td>';
5843 print $langs->trans('RetainedWarrantyDateLimit');
5844 print '</td>';
5845 if ($action != 'editretainedwarrantydatelimit' && $user->hasRight('facture', 'creer') && $object->status == Facture::STATUS_DRAFT) {
5846 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>';
5847 }
5848
5849 print '</tr></table>';
5850 print '</td><td>';
5851 $defaultDate = !empty($object->retained_warranty_date_limit) ? $object->retained_warranty_date_limit : strtotime('-1 years', $object->date_lim_reglement);
5852 if ($object->date > $defaultDate) {
5853 $defaultDate = $object->date;
5854 }
5855
5856 if ($action == 'editretainedwarrantydatelimit' && $object->status == Facture::STATUS_DRAFT) {
5857 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'">';
5858 print '<input type="hidden" name="action" value="setretainedwarrantydatelimit">';
5859 print '<input type="hidden" name="token" value="'.newToken().'">';
5860 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5861 //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
5862 print $form->selectDate($defaultDate, 'retained_warranty_date_limit');
5863 print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
5864 print '</form>';
5865 } else {
5866 print dol_print_date($object->retained_warranty_date_limit, 'day');
5867 }
5868 print '</td></tr>';
5869 }
5870 }
5871
5872
5873 // Other attributes
5874 $cols = 2;
5875 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
5876
5877 print '</table>';
5878
5879 print '</div>';
5880 print '<div class="fichehalfright">';
5881
5882 print '<!-- amounts -->'."\n";
5883 print '<div class="underbanner clearboth"></div>'."\n";
5884
5885 print '<table class="border tableforfield centpercent">';
5886
5887 include DOL_DOCUMENT_ROOT.'/core/tpl/object_currency_amount.tpl.php';
5888
5889 $sign = 1;
5890 if (getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE_SCREEN') && $object->type == $object::TYPE_CREDIT_NOTE) {
5891 $sign = -1; // We invert sign for output
5892 }
5893 print '<tr>';
5894 // Amount HT
5895 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
5896 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_ht, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
5897 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
5898 // Multicurrency Amount HT
5899 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_ht, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
5900 }
5901 print '</tr>';
5902
5903 print '<tr>';
5904 // Amount VAT
5905 print '<td>' . $langs->trans('AmountVAT') . '</td>';
5906 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_tva, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
5907 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
5908 // Multicurrency Amount VAT
5909 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_tva, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
5910 }
5911 print '</tr>';
5912
5913 // Amount Local Taxes
5914 if (($mysoc->localtax1_assuj == "1" && $mysoc->useLocalTax(1)) || $object->total_localtax1 != 0) {
5915 print '<tr>';
5916 print '<td class="titlefieldmiddle">' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
5917 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
5918 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
5919 $object->multicurrency_total_localtax1 = (float) price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
5920
5921 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_localtax1, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
5922 }
5923 print '</tr>';
5924 }
5925
5926 if (($mysoc->localtax2_assuj == "1" && $mysoc->useLocalTax(2)) || $object->total_localtax2 != 0) {
5927 print '<tr>';
5928 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
5929 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
5930 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
5931 $object->multicurrency_total_localtax2 = (float) price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
5932
5933 print '<td class="nowraponall amountcard right">' . price($sign * $object->multicurrency_total_localtax2, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
5934 }
5935 print '</tr>';
5936 }
5937
5938 // Add the revenue stamp
5939 if ($selleruserevenustamp) {
5940 print '<tr><td class="titlefieldmiddle">';
5941 print '<table class="nobordernopadding centpercent"><tr><td>';
5942 print $langs->trans('RevenueStamp');
5943 print '</td>';
5944 if ($action != 'editrevenuestamp' && $object->status == $object::STATUS_DRAFT && $usercancreate) {
5945 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>';
5946 }
5947 print '</tr></table>';
5948 print '</td><td class="nowrap amountcard right">';
5949 if ($action == 'editrevenuestamp') {
5950 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
5951 print '<input type="hidden" name="token" value="'.newToken().'">';
5952 print '<input type="hidden" name="action" value="setrevenuestamp">';
5953 print '<input type="hidden" name="revenuestamp" id="revenuestamp_val" value="'.price2num($object->revenuestamp).'">';
5954 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
5955 print $formother->select_revenue_stamp('', 'revenuestamp_type', $mysoc->country_code);
5956 print ' &rarr; <span id="revenuestamp_span"></span>';
5957 print ' <input type="submit" class="button buttongen button-save small" value="'.$langs->trans('Modify').'">';
5958 print '</form>';
5959 print " <script>
5960 $(document).ready(function(){
5961 js_recalculate_revenuestamp();
5962 $('select[name=revenuestamp_type]').on('change',function(){
5963 js_recalculate_revenuestamp();
5964 });
5965 });
5966 function js_recalculate_revenuestamp(){
5967 var valselected = $('select[name=revenuestamp_type]').val();
5968 console.log('Calculate revenue stamp from '+valselected);
5969 var revenue = 0;
5970 if (valselected.indexOf('%') == -1)
5971 {
5972 revenue = valselected;
5973 }
5974 else
5975 {
5976 var revenue_type = parseFloat(valselected);
5977 var amount_net = ".round($object->total_ht, 2).";
5978 revenue = revenue_type * amount_net / 100;
5979 revenue = revenue.toFixed(2);
5980 }
5981 $('#revenuestamp_val').val(revenue);
5982 $('#revenuestamp_span').html(revenue);
5983 }
5984 </script>";
5985 } else {
5986 print price($object->revenuestamp, 1, '', 1, -1, -1, $conf->currency);
5987 }
5988 print '</td></tr>';
5989 }
5990
5991 print '<tr>';
5992 // Amount TTC
5993 print '<td>' . $langs->trans('AmountTTC') . '</td>';
5994 print '<td class="nowraponall amountcard right">' . price($sign * $object->total_ttc, 0, $langs, 0, -1, -1, $conf->currency) . '</td>';
5995 if (isModEnabled("multicurrency") && $object->multicurrency_code && $object->multicurrency_code != $conf->currency) {
5996 // Multicurrency Amount TTC
5997 print '<td class="nowrap amountcard right">' . price($sign * $object->multicurrency_total_ttc, 0, $langs, 0, -1, -1, $object->multicurrency_code) . '</td>';
5998 }
5999 print '</tr>';
6000
6001 print '</table>';
6002
6003 $nbrows = 8;
6004 $nbcols = 3;
6005 if (isModEnabled('project')) {
6006 $nbrows++;
6007 }
6008 if (isModEnabled("bank")) {
6009 $nbrows++;
6010 $nbcols++;
6011 }
6012 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
6013 $nbrows++;
6014 }
6015 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
6016 $nbrows++;
6017 }
6018 if ($selleruserevenustamp) {
6019 $nbrows++;
6020 }
6021 if (isModEnabled('multicurrency')) {
6022 $nbrows += 5;
6023 }
6024 if (isModEnabled('incoterm')) {
6025 $nbrows += 1;
6026 }
6027
6028 $total_prev_ht = $total_prev_ttc = 0;
6029 $total_global_ht = $total_global_ttc = 0;
6030
6031 // List of previous situation invoices
6032 if (($object->situation_cycle_ref > 0) && getDolGlobalString('INVOICE_USE_SITUATION')) {
6033 print '<!-- List of situation invoices -->';
6034 print '<div class="div-table-responsive-no-min">';
6035 print '<table class="noborder paymenttable centpercent situationstable">';
6036
6037 print '<tr class="liste_titre">';
6038 print '<td>'.$langs->trans('ListOfSituationInvoices').'</td>';
6039 print '<td></td>';
6040 print '<td class="center">'.$langs->trans('Situation').'</td>';
6041
6042 if (isModEnabled("bank")) {
6043 print '<td class="right"></td>';
6044 }
6045 print '<td class="right">'.$langs->trans('AmountHT').'</td>';
6046 print '<td class="right">'.$langs->trans('AmountTTC').'</td>';
6047 print '<td width="18">&nbsp;</td>';
6048 print '</tr>';
6049
6050 if (count($object->tab_previous_situation_invoice) > 0) {
6051 // List of previous invoices
6052
6053 $current_situation_counter = array();
6054 foreach ($object->tab_previous_situation_invoice as $prev_invoice) {
6055 $tmptotalallpayments = $prev_invoice->getSommePaiement(0);
6056 $tmptotalallpayments += $prev_invoice->getSumDepositsUsed(0);
6057 $tmptotalallpayments += $prev_invoice->getSumCreditNotesUsed(0);
6058
6059 $total_prev_ht += $prev_invoice->total_ht;
6060 $total_prev_ttc += $prev_invoice->total_ttc;
6061
6062 $current_situation_counter[] = (($prev_invoice->type == Facture::TYPE_CREDIT_NOTE) ? -1 : 1) * $prev_invoice->situation_counter;
6063 print '<tr class="oddeven">';
6064 print '<td>'.$prev_invoice->getNomUrl(1).'</td>';
6065 print '<td></td>';
6066 print '<td align="center" >'.(($prev_invoice->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$prev_invoice->situation_counter.'</td>';
6067 if (isModEnabled("bank")) {
6068 print '<td class="right"></td>';
6069 }
6070 print '<td class="right"><span class="amount">'.price($prev_invoice->total_ht).'</span></td>';
6071 print '<td class="right"><span class="amount">'.price($prev_invoice->total_ttc).'</span></td>';
6072 print '<td class="right">'.$prev_invoice->getLibStatut(3, $tmptotalallpayments).'</td>';
6073 print '</tr>';
6074 }
6075 }
6076
6077 $totalallpayments = $object->getSommePaiement(0);
6078 $totalallpayments += $object->getSumCreditNotesUsed(0);
6079 $totalallpayments += $object->getSumDepositsUsed(0);
6080
6081 $total_global_ht += $total_prev_ht;
6082 $total_global_ttc += $total_prev_ttc;
6083 $total_global_ht += $object->total_ht;
6084 $total_global_ttc += $object->total_ttc;
6085
6086 $current_situation_counter[] = (($object->type == Facture::TYPE_CREDIT_NOTE) ? -1 : 1) * $object->situation_counter;
6087
6088 print '<tr class="oddeven">';
6089 print '<td>'.$object->getNomUrl(1).'</td>';
6090 print '<td></td>';
6091 print '<td class="center">'.(($object->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$object->situation_counter.'</td>';
6092 if (isModEnabled("bank")) {
6093 print '<td class="right"></td>';
6094 }
6095 print '<td class="right"><span class="amount">'.price($object->total_ht).'</span></td>';
6096 print '<td class="right"><span class="amount">'.price($object->total_ttc).'</span></td>';
6097 print '<td class="right">'.$object->getLibStatut(3, $totalallpayments).'</td>';
6098 print '</tr>';
6099
6100
6101 print '<tr class="oddeven">';
6102 print '<td colspan="2" class="left"><b>'.$langs->trans('SituationTotalAfterInvoice').'</b></td>';
6103 print '<td>';
6104 $i = 0;
6105 foreach ($current_situation_counter as $sit) {
6106 $curSign = $sit > 0 ? '+' : '-';
6107 $curType = $sit > 0 ? $langs->trans('situationInvoiceShortcode_S') : $langs->trans('situationInvoiceShortcode_AS');
6108 if ($i > 0) {
6109 print ' '.$curSign.' ';
6110 }
6111 print $curType.abs($sit);
6112 $i++;
6113 }
6114 print '</td>';
6115 if (isModEnabled("bank")) {
6116 print '<td></td>';
6117 }
6118 print '<td class="right"><b>'.price($total_global_ht).'</b></td>';
6119 print '<td class="right"><b>'.price($total_global_ttc).'</b></td>';
6120 print '<td width="18">&nbsp;</td>';
6121 print '</tr>';
6122
6123
6124 if (count($object->tab_next_situation_invoice) > 0) {
6125 // List of next invoices
6126 $total_next_ht = $total_next_ttc = 0;
6127
6128 foreach ($object->tab_next_situation_invoice as $next_invoice) {
6129 $totalpaid = $next_invoice->getSommePaiement(0);
6130 $totalcreditnotes = $next_invoice->getSumCreditNotesUsed(0);
6131 $totaldeposits = $next_invoice->getSumDepositsUsed(0);
6132 $total_next_ht += $next_invoice->total_ht;
6133 $total_next_ttc += $next_invoice->total_ttc;
6134
6135 print '<tr class="oddeven">';
6136 print '<td>'.$next_invoice->getNomUrl(1).'</td>';
6137 print '<td></td>';
6138 print '<td class="center">'.(($next_invoice->type == Facture::TYPE_CREDIT_NOTE) ? $langs->trans('situationInvoiceShortcode_AS') : $langs->trans('situationInvoiceShortcode_S')).$next_invoice->situation_counter.'</td>';
6139 if (isModEnabled("bank")) {
6140 print '<td class="right"></td>';
6141 }
6142 print '<td class="right"><span class="amount">'.price($next_invoice->total_ht).'</span></td>';
6143 print '<td class="right"><span class="amount">'.price($next_invoice->total_ttc).'</span></td>';
6144 print '<td class="right">'.$next_invoice->getLibStatut(3, $totalpaid + $totalcreditnotes + $totaldeposits).'</td>';
6145 print '</tr>';
6146 }
6147
6148 $total_global_ht += $total_next_ht;
6149 $total_global_ttc += $total_next_ttc;
6150
6151 print '<tr class="oddeven">';
6152 print '<td colspan="3" class="right"></td>';
6153 if (isModEnabled("bank")) {
6154 print '<td class="right"></td>';
6155 }
6156 print '<td class="right"><b>'.price($total_global_ht).'</b></td>';
6157 print '<td class="right"><b>'.price($total_global_ttc).'</b></td>';
6158 print '<td width="18">&nbsp;</td>';
6159 print '</tr>';
6160 }
6161
6162 print '</table>';
6163 print '</div>';
6164 }
6165
6166 $sign = 1;
6167 if ($object->type == $object::TYPE_CREDIT_NOTE) {
6168 $sign = -1;
6169 }
6170
6171 // List of payments already done
6172
6173 print '<!-- List of payments already done -->';
6174 print '<div class="div-table-responsive-no-min">';
6175 print '<table class="noborder paymenttable centpercent">';
6176
6177 print '<tr class="liste_titre">';
6178 print '<td class="liste_titre">'.($object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("PaymentsBack") : $langs->trans('Payments')).'</td>';
6179 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('Date').'</span></td>';
6180 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('Type').'</span></td>';
6181 if (isModEnabled("bank")) {
6182 print '<td class="liste_titre"><span class="hideonsmartphone">'.$langs->trans('BankAccount').'</span></td>';
6183 }
6184 print '<td class="liste_titre"></td>';
6185 print '<td class="liste_titre right">'.$langs->trans('Amount').'</td>';
6186 print '</tr>';
6187
6188 // Payments already done (from payment on this invoice)
6189 $sql = 'SELECT p.datep as dp, p.ref, p.num_paiement as num_payment, p.rowid, p.fk_bank,';
6190 $sql .= ' c.code as payment_code, c.libelle as payment_label,';
6191 $sql .= ' pf.amount,';
6192 $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';
6193 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
6194 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
6195 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON p.fk_bank = b.rowid';
6196 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank_account as ba ON b.fk_account = ba.rowid';
6197 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id).' AND pf.fk_paiement = p.rowid';
6198 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
6199 $sql .= ' ORDER BY p.datep, p.tms';
6200
6201 $result = $db->query($sql);
6202 if ($result) {
6203 $num = $db->num_rows($result);
6204 $i = 0;
6205
6206 if ($num > 0) {
6207 while ($i < $num) {
6208 $objp = $db->fetch_object($result);
6209
6210 $paymentstatic->id = $objp->rowid;
6211 $paymentstatic->datepaye = $db->jdate($objp->dp);
6212 $paymentstatic->ref = $objp->ref;
6213 $paymentstatic->num_payment = $objp->num_payment;
6214 $paymentstatic->paiementcode = $objp->payment_code;
6215
6216 print '<tr class="oddeven">';
6217 print '<td class="nowraponall">';
6218 print $paymentstatic->getNomUrl(1);
6219 print '</td>';
6220 print '<td>';
6221 $dateofpayment = $db->jdate($objp->dp);
6222 $tmparray = dol_getdate($dateofpayment);
6223 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
6224 print dol_print_date($dateofpayment, 'day');
6225 } else { // Hours was set to real date of payment (special case for POS for example)
6226 print dol_print_date($dateofpayment, 'dayhour', 'tzuser');
6227 }
6228 print '</td>';
6229
6230 $label = ($langs->trans("PaymentType".$objp->payment_code) != "PaymentType".$objp->payment_code) ? $langs->trans("PaymentType".$objp->payment_code) : $objp->payment_label;
6231 print '<td class="tdoverflowmax80" title="'.dol_escape_htmltag($label.' '.$objp->num_payment).'">'.dol_escape_htmltag($label.' '.$objp->num_payment).'</td>';
6232 if (isModEnabled("bank")) {
6233 $bankaccountstatic->id = $objp->baid;
6234 $bankaccountstatic->ref = $objp->baref;
6235 $bankaccountstatic->label = $objp->baref;
6236 $bankaccountstatic->number = $objp->banumber;
6237 $bankaccountstatic->currency_code = $objp->bacurrency_code;
6238
6239 if (isModEnabled('accounting')) {
6240 $bankaccountstatic->account_number = $objp->account_number;
6241
6242 $accountingjournal = new AccountingJournal($db);
6243 $accountingjournal->fetch($objp->fk_accountancy_journal);
6244 $bankaccountstatic->accountancy_journal = $accountingjournal->getNomUrl(0, 1, 1, '', 1);
6245 }
6246
6247 print '<td class="nowraponall">';
6248 if ($bankaccountstatic->id) {
6249 print $bankaccountstatic->getNomUrl(1, 'transactions');
6250 }
6251 print '</td>';
6252 }
6253
6254 // Delete
6255 print '<td class="center">';
6256 $paiement = new Paiement($db);
6257 $paiement->fetch($objp->rowid);
6258 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $user->socid == 0 && !$paiement->isReconciled()) {
6259 print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deletepayment&token='.newToken().'&paiement_id='.$objp->rowid.'">';
6260 print img_delete();
6261 print '</a>';
6262 }
6263 print '</td>';
6264
6265 // Amount
6266 print '<td class="right"><span class="amount">'.price($sign * $objp->amount).'</span></td>';
6267
6268 print '</tr>';
6269 $i++;
6270 }
6271 }
6272
6273 $db->free($result);
6274 } else {
6276 }
6277
6278 if ($object->type != Facture::TYPE_CREDIT_NOTE) {
6279 // Total already paid
6280 print '<tr>';
6281 print '<td colspan="'.($nbcols + 1).'" class="right">';
6282 print '<span class="opacitymedium">';
6283 if ($object->type != Facture::TYPE_DEPOSIT) {
6284 print $langs->trans('AlreadyPaidNoCreditNotesNoDeposits');
6285 } else {
6286 print $langs->trans('AlreadyPaid');
6287 }
6288 print '</span></td>';
6289 //print '<td></td>';
6290 print '<td class="right'.(($totalpaid > 0) ? ' amountalreadypaid' : '').'">'.price($totalpaid).'</td>';
6291 print '</tr>';
6292
6293 $resteapayeraffiche = $resteapayer;
6294 $cssforamountpaymentcomplete = 'amountpaymentcomplete';
6295 $cssforamountpaymentcompletenoresize = 'amountpaymentcompletenoresize';
6296
6297 // Loop on each credit note or deposit amount applied
6298 $creditnoteamount = 0;
6299 $depositamount = 0;
6300 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
6301 $sql .= " re.description, re.fk_facture_source";
6302 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
6303 $sql .= " WHERE fk_facture = ".((int) $object->id);
6304 $resql = $db->query($sql);
6305 if ($resql) {
6306 $num = $db->num_rows($resql);
6307 $i = 0;
6308 $invoice = new Facture($db);
6309 while ($i < $num) {
6310 $obj = $db->fetch_object($resql);
6311 $invoice->fetch($obj->fk_facture_source);
6312 print '<tr>';
6313 print '<td colspan="'.$nbcols.'" class="right">';
6314 print '<span class="opacitymedium">';
6315 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
6316 print $langs->trans("CreditNote").' ';
6317 }
6318 if ($invoice->type == Facture::TYPE_DEPOSIT) {
6319 print $langs->trans("Deposit").' ';
6320 }
6321 print $invoice->getNomUrl(0);
6322 print '</span>';
6323 print '</td>';
6324 // Delete discount
6325 print '<td class="right">';
6326 print '<a href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=unlinkdiscount&token='.newToken().'&discountid='.$obj->rowid.'">';
6327 print img_picto($langs->transnoentitiesnoconv("RemoveDiscount"), 'unlink');
6328 print '</a>';
6329 print '</td>';
6330 // Amount
6331 print '<td class="right"><span class="amount">'.price($obj->amount_ttc).'</span></td>';
6332 print '</tr>';
6333 $i++;
6334 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
6335 $creditnoteamount += $obj->amount_ttc;
6336 }
6337 if ($invoice->type == Facture::TYPE_DEPOSIT) {
6338 $depositamount += $obj->amount_ttc;
6339 }
6340 }
6341 } else {
6343 }
6344
6345 // Partially paid 'discount'
6346 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'discount_vat') {
6347 print '<tr>';
6348 print '<td colspan="'.$nbcols.'" class="nowrap right">';
6349 print '<span class="opacitymedium">';
6350 print $form->textwithpicto($langs->trans("Discount"), $langs->trans("HelpEscompte"), - 1);
6351 print '</span>';
6352 print '</td>';
6353 print '<td></td>';
6354 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6355 print '</tr>';
6356 $resteapayeraffiche = 0;
6357 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6358 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6359 }
6360 // Partially paid or abandoned 'badcustomer'
6361 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'badcustomer') {
6362 print '<tr>';
6363 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6364 print '<span class="opacitymedium">';
6365 print $form->textwithpicto($langs->trans("Abandoned"), $langs->trans("HelpAbandonBadCustomer"), - 1);
6366 print '</span>';
6367 print '</td>';
6368 //print '<td></td>';
6369 print '<td class="right">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</td>';
6370 print '</tr>';
6371 // $resteapayeraffiche=0;
6372 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6373 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6374 }
6375 // Partially paid or abandoned 'product_returned'
6376 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'product_returned') {
6377 print '<tr>';
6378 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6379 print '<span class="opacitymedium">';
6380 print $form->textwithpicto($langs->trans("ProductReturned"), $langs->trans("HelpAbandonProductReturned"), - 1);
6381 print '</span>';
6382 print '</td>';
6383 //print '<td></td>';
6384 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6385 print '</tr>';
6386 $resteapayeraffiche = 0;
6387 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6388 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6389 }
6390 // Partially paid or abandoned 'abandoned'
6391 if (($object->status == Facture::STATUS_CLOSED || $object->status == Facture::STATUS_ABANDONED) && $object->close_code == 'abandon') {
6392 print '<tr>';
6393 print '<td colspan="'.($nbcols + 1).'" class="nowrap right">';
6394 $text = $langs->trans("HelpAbandonOther");
6395 if ($object->close_note) {
6396 $text .= '<br><br><b>'.$langs->trans("Reason").'</b>:'.$object->close_note;
6397 }
6398 print '<span class="opacitymedium">';
6399 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6400 print $form->textwithpicto($langs->trans("Abandoned"), $text, - 1);
6401 print '</span>';
6402 print '</td>';
6403 //print '<td></td>';
6404 print '<td class="right"><span class="amount">'.price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaid, 'MT')).'</span></td>';
6405 print '</tr>';
6406 $resteapayeraffiche = 0;
6407 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6408 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6409 }
6410
6411 // Billed
6412 print '<tr>';
6413 print '<td colspan="'.($nbcols + 1).'" class="right">';
6414 print '<span class="opacitymedium">';
6415 print $langs->trans("Billed");
6416 print '</td>';
6417 //print '<td></td>';
6418 print '<td class="right">'.price($object->total_ttc).'</td>';
6419 print '</tr>';
6420
6421 // Remainder to pay
6422 print '<tr>';
6423 print '<td colspan="'.($nbcols + 1).'" class="right">';
6424 print '<span class="opacitymedium">';
6425 print $langs->trans('RemainderToPay');
6426 if ($resteapayeraffiche < 0) {
6427 print ' ('.$langs->trans('NegativeIfExcessReceived').')';
6428 }
6429 print '</span>';
6430 print '</td>';
6431 //print '<td></td>';
6432 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopay' : (' '.$cssforamountpaymentcomplete)).'">'.price($resteapayeraffiche).'</td>';
6433 print '</tr>';
6434
6435 // Remainder to pay Multicurrency
6436 if (isModEnabled('multicurrency') && $object->multicurrency_code && ($object->multicurrency_code != $conf->currency || $object->multicurrency_tx != 1)) {
6437 print '<tr>';
6438 print '<td colspan="'.($nbcols + 1).'" class="right">';
6439 print '<span class="opacitymedium">';
6440 print $langs->trans('RemainderToPayMulticurrency');
6441 if ($resteapayeraffiche < 0) {
6442 print ' ('.$langs->trans('NegativeIfExcessReceived').')';
6443 }
6444 print '</span>';
6445 print '</td>';
6446 //print '<td></td>';
6447 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopaynoresize' : (' '.$cssforamountpaymentcompletenoresize)).'">';
6448 //print (empty($object->multicurrency_code) ? $conf->currency : $object->multicurrency_code).' ';
6449 print price(price2num($object->multicurrency_tx * $resteapayeraffiche, 'MT'), 1, $langs, 1, -1, -1, (empty($object->multicurrency_code) ? $conf->currency : $object->multicurrency_code)).'</td>';
6450 print '</tr>';
6451 }
6452
6453 // Retained warranty : usually use on construction industry
6454 if (!empty($object->situation_final) && !empty($object->retained_warranty) && $displayWarranty) {
6455 // Billed - retained warranty
6456 if ($object->isSituationInvoice()) {
6457 $retainedWarranty = $total_global_ttc * $object->retained_warranty / 100;
6458 } else {
6459 // Because one day retained warranty could be used on standard invoices
6460 $retainedWarranty = $object->total_ttc * $object->retained_warranty / 100;
6461 }
6462
6463 $billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
6464
6465 print '<tr>';
6466 print '<td colspan="'.($nbcols + 1).'" class="right">'.$langs->trans("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')).' :</td>';
6467 //print '<td></td>';
6468 print '<td class="right">'.price($billedWithRetainedWarranty).'</td>';
6469 print '</tr>';
6470
6471 // retained warranty
6472 print '<tr>';
6473 print '<td colspan="'.($nbcols + 1).'" class="right">';
6474 print $langs->trans("RetainedWarranty").' ('.$object->retained_warranty.'%)';
6475 print !empty($object->retained_warranty_date_limit) ? ' '.$langs->trans("ToPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
6476 print '</td>';
6477 //print '<td></td>';
6478 print '<td class="right">'.price($retainedWarranty).'</td>';
6479 print '</tr>';
6480 }
6481 } else { // Credit note
6482 $resteapayeraffiche = $resteapayer;
6483 $cssforamountpaymentcomplete = 'amountpaymentneutral';
6484 $cssforamountpaymentcompletenoresize = 'amountpaymentneutralnoresize';
6485
6486 // Total already paid back
6487 print '<tr>';
6488 print '<td colspan="'.($nbcols + 1).'" class="right">';
6489 print '<span class="opacitymedium">'.$langs->trans('AlreadyPaidBack').'</span>';
6490 print '</td>';
6491 //print '<td></td>';
6492 print '<td class="right"><span class="amount">'.price($sign * $totalpaid).'</span></td>';
6493 print '</tr>';
6494
6495 // Billed
6496 print '<tr>';
6497 print '<td colspan="'.($nbcols + 1).'" class="right"><span class="opacitymedium">'.$langs->trans("Billed").'</span></td>';
6498 //print '<td></td>';
6499 print '<td class="right">'.price($sign * $object->total_ttc).'</td>';
6500 print '</tr>';
6501
6502 // Remainder to pay back
6503 print '<tr><td colspan="'.($nbcols + 1).'" class="right">';
6504 print '<span class="opacitymedium">'.$langs->trans('RemainderToPayBack');
6505 if ($resteapayeraffiche > 0) {
6506 print ' ('.$langs->trans('NegativeIfExcessRefunded').')';
6507 }
6508 print '</span></td>';
6509 //print '<td class="nowrap"></td>';
6510 print '<td class="right'.($resteapayeraffiche ? ' amountremaintopayback' : (' '.$cssforamountpaymentcomplete)).'">'.price($sign * $resteapayeraffiche);
6511 // TODO If credit not was converted into discount, we should show a tooltip to explain that remain to pay
6512 // is zero because already converted into discount for a future use, so no need to refund.
6513 print '</td>';
6514 print '</tr>';
6515
6516 // Remainder to pay back Multicurrency
6517 if (isModEnabled('multicurrency') && (($object->multicurrency_code && $object->multicurrency_code != $conf->currency) || $object->multicurrency_tx != 1)) {
6518 print '<tr>';
6519 print '<td colspan="'.($nbcols + 1).'" class="right">';
6520 print '<span class="opacitymedium">'.$langs->trans('RemainderToPayBackMulticurrency');
6521 if ($resteapayeraffiche > 0) {
6522 print ' ('.$langs->trans('NegativeIfExcessRefunded').')';
6523 }
6524 print '</span>';
6525 print '</td>';
6526 //print '<td></td>';
6527 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>';
6528 print '</tr>';
6529 }
6530
6531 // Sold credit note
6532 // print '<tr><td colspan="'.$nbcols.'" class="right">'.$langs->trans('TotalTTC').' :</td>';
6533 // print '<td class="right" style="border: 1px solid;" bgcolor="#f0f0f0"><b>'.price($sign *
6534 // $object->total_ttc).'</b></td><td>&nbsp;</td></tr>';
6535 }
6536
6537 print '</table>';
6538 print '</div>';
6539
6540 // Margin Infos
6541 if (isModEnabled('margin')) {
6542 $formmargin->displayMarginInfos($object);
6543 }
6544
6545 print '</div>';
6546 print '</div>';
6547
6548 print '<div class="clearboth"></div><br>';
6549
6550 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
6551 $blocname = 'contacts';
6552 $title = $langs->trans('ContactsAddresses');
6553 include DOL_DOCUMENT_ROOT.'/core/tpl/bloc_showhide.tpl.php';
6554 }
6555
6556 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
6557 $blocname = 'notes';
6558 $title = $langs->trans('Notes');
6559 include DOL_DOCUMENT_ROOT.'/core/tpl/bloc_showhide.tpl.php';
6560 }
6561
6562 // Get object lines
6563 $result = $object->getLinesArray();
6564
6565 // Add products/services form
6566 //$forceall = 1;
6567 global $inputalsopricewithtax;
6568 $inputalsopricewithtax = 1;
6569
6570 // Show global modifiers for situation invoices
6571 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
6572 if ($object->situation_cycle_ref && $object->status == 0) {
6573 print '<!-- Area to change globally the situation percent -->'."\n";
6574 print '<div class="div-table-responsive-no-min">';
6575
6576 print '<form name="updatealllines" id="updatealllines" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'#updatealllines" method="POST">';
6577 print '<input type="hidden" name="token" value="'.newToken().'" />';
6578 print '<input type="hidden" name="action" value="updatealllines" />';
6579 print '<input type="hidden" name="id" value="'.$object->id.'" />';
6580 print '<input type="hidden" name="page_y" value="" />';
6581 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
6582
6583 print '<table id="tablelines_all_progress" class="noborder noshadow centpercent">';
6584
6585 print '<tr class="liste_titre nodrag nodrop">';
6586
6587 // Adds a line numbering column
6588 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
6589 print '<td align="center" width="5">&nbsp;</td>';
6590 }
6591 print '<td class="minwidth500imp">'.$langs->trans('ModifyAllLines').'</td>';
6592 print '<td class="right">'.$langs->trans('CumulativeProgression').'</td>';
6593 print '<td>&nbsp;</td>';
6594 print "</tr>\n";
6595
6596 print '<tr class="nodrag nodrop">';
6597 // Adds a line numbering column
6598 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
6599 print '<td align="center" width="5">&nbsp;</td>';
6600 }
6601 print '<td>&nbsp;</td>';
6602 print '<td class="nowrap right"><input type="text" size="1" value="" name="all_progress">%</td>';
6603 print '<td class="right"><input type="submit" class="button reposition small" name="all_percent" value="'.$langs->trans("Modify").'" /></td>';
6604 print '</tr>';
6605
6606 print '</table>';
6607
6608 print '</form>';
6609
6610 print '</div>';
6611 }
6612 }
6613
6614 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">
6615 <input type="hidden" name="token" value="' . newToken().'">
6616 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
6617 <input type="hidden" name="mode" value="">
6618 <input type="hidden" name="page_y" value="">
6619 <input type="hidden" name="id" value="' . $object->id.'">
6620 <input type="hidden" name="backtopage" value="'.$backtopage.'">
6621 ';
6622
6623 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
6624 if (isModEnabled('subtotals')) {
6625 include DOL_DOCUMENT_ROOT.'/core/tpl/subtotal_ajaxrow.tpl.php';
6626 } else {
6627 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
6628 }
6629 }
6630
6631 print '<div class="div-table-responsive-no-min">';
6632 print '<table id="tablelines" class="noborder noshadow centpercent nomarginbottom">';
6633
6634 // Show object lines
6635 if (!empty($object->lines)) {
6636 $object->printObjectLines($action, $mysoc, $soc, $lineid, 1);
6637 }
6638
6639 // Form to add new line
6640 if ($object->status == 0 && $usercancreate && $action != 'valid') {
6641 if ($action != 'editline' && $action != 'selectlines') {
6642 // Add free products/services
6643
6644 $parameters = array();
6645 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6646 if ($reshook < 0) {
6647 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
6648 }
6649 if (empty($reshook)) {
6650 $object->formAddObjectLine(1, $mysoc, $soc);
6651 }
6652 } else {
6653 $parameters = array();
6654 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6655 }
6656 }
6657
6658 print "</table>\n";
6659 print "</div>";
6660
6661 print "</form>\n";
6662 }
6663 print dol_get_fiche_end();
6664
6665
6666 // Actions buttons
6667
6668 if ($action != 'prerelance' && $action != 'presend' && $action != 'valid' && $action != 'editline') {
6669 print '<div class="tabsAction">';
6670
6671 $parameters = array();
6672 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
6673 if (empty($reshook)) {
6674 $params = array(
6675 'attr' => array(
6676 'class' => 'classfortooltip',
6677 'title' => ''
6678 )
6679 );
6680 // Edit a validated invoice without any payment and not transferred to accounting
6681 if ($object->status == Facture::STATUS_VALIDATED && !getDolGlobalString('INVOICE_CAN_NEVER_BE_EDITED')) {
6682 // We check if lines of invoice are not already transferred into accountancy
6683 $ventilExportCompta = $object->getVentilExportCompta();
6684
6685 if ($ventilExportCompta == 0) {
6686 if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == price2num($object->total_ttc, 'MT', 1) && empty($object->paye))) {
6687 if (!$objectidnext && $object->is_last_in_cycle()) {
6688 if ($usercanunvalidate) {
6689 unset($params['attr']['title']);
6690 print dolGetButtonAction($langs->trans('Modify'), '', 'default', $_SERVER['PHP_SELF'] . '?facid=' . $object->id . '&action=modif&token=' . newToken(), '', true, $params);
6691 } else {
6692 $params['attr']['title'] = $langs->trans('NotEnoughPermissions');
6693 print dolGetButtonAction($langs->trans('Modify'), '', 'default', $_SERVER['PHP_SELF'] . '?facid=' . $object->id . '&action=modif&token=' . newToken(), '', false, $params);
6694 }
6695 } elseif (!$object->is_last_in_cycle()) {
6696 $params['attr']['title'] = $langs->trans('NotLastInCycle');
6697 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6698 } else {
6699 $params['attr']['title'] = $langs->trans('DisabledBecauseReplacedInvoice');
6700 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6701 }
6702 }
6703 } else {
6704 $params['attr']['title'] = $langs->trans('DisabledBecauseDispatchedInBookkeeping');
6705 print dolGetButtonAction($langs->trans('Modify'), '', 'default', '#', '', false, $params);
6706 }
6707 }
6708
6709 $discount = new DiscountAbsolute($db);
6710 $result = $discount->fetch(0, $object->id);
6711
6712 // Reopen an invoice
6714 || ($object->type == Facture::TYPE_CREDIT_NOTE && empty($discount->id))
6715 || ($object->type == Facture::TYPE_DEPOSIT && empty($discount->id))
6716 || ($object->type == Facture::TYPE_SITUATION && empty($discount->id)))
6717 && ($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
6718 && ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || $usercanreopen)) { // A paid invoice (partially or completely)
6719 if ($object->close_code != 'replaced' || (!$objectidnext)) { // Not replaced by another invoice or replaced but the replacement invoice has been deleted
6720 unset($params['attr']['title']);
6721 print dolGetButtonAction($langs->trans('ReOpen'), '', 'default', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=reopen&token='.newToken(), '', true, $params);
6722 } else {
6723 $params['attr']['title'] = $langs->trans("DisabledBecauseReplacedInvoice");
6724 print dolGetButtonAction($langs->trans('ReOpen'), '', 'default', '#', '', false, $params);
6725 }
6726 }
6727
6728 // Create contract
6729 if (getDolGlobalString('CONTRACT_CREATE_FROM_INVOICE')) {
6730 if (isModEnabled('contract') && $object->status == Facture::STATUS_VALIDATED) {
6731 $langs->load("contracts");
6732
6733 if ($usercancreatecontract) {
6734 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>';
6735 }
6736 }
6737 }
6738
6739 // Subtotal
6740 if ($object->status == Facture::STATUS_DRAFT && isModEnabled('subtotals')
6741 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
6742 $langs->load("subtotals");
6743
6744 $url_button = array();
6745
6746 $url_button[] = array(
6747 'lang' => 'subtotals',
6748 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
6749 'perm' => (bool) $usercancreate,
6750 'label' => $langs->trans('AddTitleLine'),
6751 'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_title_line&token='.newToken()
6752 );
6753
6754 $url_button[] = array(
6755 'lang' => 'subtotals',
6756 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
6757 'perm' => (bool) $usercancreate,
6758 'label' => $langs->trans('AddSubtotalLine'),
6759 'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_subtotal_line&token='.newToken()
6760 );
6761 print dolGetButtonAction('', $langs->trans('Subtotal'), 'default', $url_button, '', true);
6762 }
6763
6764 // Validate
6765 if ($object->status == Facture::STATUS_DRAFT && count($object->lines) > 0
6766 && ((($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))
6767 || ($object->type == Facture::TYPE_CREDIT_NOTE && $object->total_ttc <= 0))) {
6768 if ($usercanvalidate) {
6769 unset($params['attr']['title']);
6770 print dolGetButtonAction($langs->trans('Validate'), '', 'default', $_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=valid&token='.newToken(), '', true, $params);
6771 }
6772 }
6773
6774 // Send by mail
6775 if (empty($user->socid)) {
6776 if (($object->status == Facture::STATUS_VALIDATED || $object->status == Facture::STATUS_CLOSED) || getDolGlobalString('FACTURE_SENDBYEMAIL_FOR_ALL_STATUS')) {
6777 if ($objectidnext) {
6778 $params['attr']['title'] = $langs->trans("DisabledBecauseReplacedInvoice");
6779 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', '#', '', false, $params);
6780 } else {
6781 if ($usercansend) {
6782 unset($params['attr']['title']);
6783 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=presend&mode=init#formmailbeforetitle', '', true, $params);
6784 } else {
6785 unset($params['attr']['title']);
6786 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', '#', '', false, $params);
6787 }
6788 }
6789 }
6790 }
6791
6792 // Request a direct debit order
6793 if ($object->status > Facture::STATUS_DRAFT && $object->paye == 0) {
6794 if ($resteapayer > 0) {
6795 if ($usercancreatewithdrarequest) {
6796 if (!$objectidnext && $object->close_code != 'replaced') { // Not replaced by another invoice
6797 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>';
6798 } else {
6799 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('MakeWithdrawRequest').'</span>';
6800 }
6801 } else {
6802 //print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
6803 }
6804 } else {
6805 //print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("AmountMustBePositive")).'">'.$langs->trans("MakeWithdrawRequest").'</a>';
6806 }
6807 }
6808
6809 // POS Ticket
6810 if (isModEnabled('takepos') && $object->module_source == 'takepos') {
6811 $langs->load("cashdesk");
6812 $receipt_url = DOL_URL_ROOT."/takepos/receipt.php";
6813 print '<a target="_blank" rel="noopener noreferrer" class="butAction" href="'.$receipt_url.'?facid='.((int) $object->id).'">'.$langs->trans('POSTicket').'</a>';
6814 }
6815
6816 // Create payment
6817 if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->status == 1 && $object->paye == 0 && $usercanissuepayment) {
6818 if ($objectidnext) {
6819 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('DoPayment').'</span>';
6820 } else {
6821 if ($object->type == Facture::TYPE_DEPOSIT && $resteapayer == 0) {
6822 // For down payment, we refuse to receive more than amount to pay.
6823 $params['attr']['title'] = $langs->trans('DisabledBecauseRemainderToPayIsZero');
6824 print dolGetButtonAction($langs->trans('DoPayment'), '', 'default', '#', '', false, $params);
6825 } else {
6826 // 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)
6827 //print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/paiement.php?facid='.$object->id.'&action=create&accountid='.$object->fk_account.'">'.$langs->trans('DoPayment').'</a>';
6828 unset($params['attr']['title']);
6829 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);
6830 }
6831 }
6832 }
6833
6834 $sumofpayment = $totalpaid;
6835 $sumofpaymentall = $totalpaid + $totalcreditnotes + $totaldeposits;
6836
6837 // Reverse back money or convert to reduction
6839 // For credit note only
6840 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $usercanissuepayment) {
6841 if ($resteapayer == 0) {
6842 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseRemainderToPayIsZero").'">'.$langs->trans('DoPaymentBack').'</span>';
6843 } else {
6844 print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/paiement.php?facid='.$object->id.'&action=create&accountid='.$object->fk_account.'">'.$langs->trans('DoPaymentBack').'</a>';
6845 }
6846 }
6847
6848 // For standard invoice with excess received
6849 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)) {
6850 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=converttoreduc&token='.newToken().'">'.$langs->trans('ConvertExcessReceivedToReduc').'</a>';
6851 }
6852 // For credit note
6853 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->status == Facture::STATUS_VALIDATED && $object->paye == 0 && $usercancreate
6854 && (getDolGlobalString('INVOICE_ALLOW_REUSE_OF_CREDIT_WHEN_PARTIALLY_REFUNDED') || $sumofpayment == 0) && $object->total_ht < 0
6855 ) {
6856 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>';
6857 }
6858
6859 // For down payment invoice (deposit)
6860 if ($object->type == Facture::TYPE_DEPOSIT && $usercancreate && $object->status > Facture::STATUS_DRAFT && empty($discount->id)) {
6861 // 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.
6862 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')))) {
6863 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=converttoreduc&token='.newToken().'">'.$langs->trans('ConvertToReduc').'</a>';
6864 } else {
6865 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("AmountPaidMustMatchAmountOfDownPayment").'">'.$langs->trans('ConvertToReduc').'</span>';
6866 }
6867 }
6868 }
6869
6870 // Classify paid
6871 if ($object->status == Facture::STATUS_VALIDATED && $usercanissuepayment && (
6872 ($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))) ||
6873 ($object->type == Facture::TYPE_CREDIT_NOTE && $resteapayer >= 0) ||
6874 ($object->type == Facture::TYPE_DEPOSIT && $object->total_ttc > 0)
6875 )
6876 ) {
6877 if ($object->type == Facture::TYPE_DEPOSIT && price2num($object->total_ttc, 'MT') != price2num($sumofpaymentall, 'MT')) {
6878 // We can close a down payment only if paid amount is same than amount of down payment (by definition)
6879 $params['attr']['title'] = $langs->trans('AmountPaidMustMatchAmountOfDownPayment');
6880 print dolGetButtonAction($langs->trans('ClassifyPaid'), '', 'default', '#', '', false, $params);
6881 } else {
6882 unset($params['attr']['title']);
6883 print dolGetButtonAction($langs->trans('ClassifyPaid'), '', 'default', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=paid&token='.newToken(), '', true, $params);
6884 }
6885 }
6886
6887 // Classify 'closed not completely paid' (possible if validated and not yet set as paid completely)
6888 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) {
6889 if ($totalpaid > 0 || $totalcreditnotes > 0) {
6890 // If one payment or one credit note was linked to this invoice
6891 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=paid&token='.newToken().'">'.$langs->trans('ClassifyPaidPartially').'</a>';
6892 } else {
6893 if (!getDolGlobalString('INVOICE_CAN_NEVER_BE_CANCELED')) {
6894 if ($objectidnext) {
6895 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("DisabledBecauseReplacedInvoice").'">'.$langs->trans('ClassifyCanceled').'</span>';
6896 } else {
6897 print '<a class="butAction'.($conf->use_javascript_ajax ? ' reposition' : '').'" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=canceled">'.$langs->trans('ClassifyCanceled').'</a>';
6898 }
6899 }
6900 }
6901 }
6902
6903 // Create next situation invoice
6904 if ($usercancreate && $object->isSituationInvoice() && ($object->status == 1 || $object->status == 2)) {
6905 if ($object->is_last_in_cycle() && $object->situation_final != 1) {
6906 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=create&type=5&origin=facture&originid='.$object->id.'&socid='.$object->socid.'" >'.$langs->trans('CreateNextSituationInvoice').'</a>';
6907 } elseif (!$object->is_last_in_cycle()) {
6908 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseNotLastInCycle").'">'.$langs->trans('CreateNextSituationInvoice').'</a>';
6909 } else {
6910 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseFinal").'">'.$langs->trans('CreateNextSituationInvoice').'</a>';
6911 }
6912 }
6913
6914 // Create a credit note
6915 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) {
6916 if (!$objectidnext) {
6917 print '<!-- button create credit note -->';
6918 if ($object->module_source == 'takepos') {
6919 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseInvoiceGeneratedBy", $langs->transnoentitiesnoconv('TakePOS')).'">'.$langs->trans("CreateCreditNote").'</a>';
6920 } else {
6921 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>';
6922 }
6923 }
6924 }
6925
6926 // For situation invoice, create credit note
6927 if ($object->status > Facture::STATUS_DRAFT
6928 && $object->isSituationInvoice()
6929 && $usercancreate
6930 && !$objectidnext
6931 && $object->is_last_in_cycle()
6932 && getDolGlobalInt('INVOICE_USE_SITUATION_CREDIT_NOTE')
6933 ) {
6934 if ($usercanunvalidate) {
6935 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>';
6936 } else {
6937 print '<span class="butActionRefused classfortooltip" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans("CreateCreditNote").'</span>';
6938 }
6939 }
6940
6941 // Clone as predefined / Create template
6942 if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA) && $object->status == 0 && $usercancreate) {
6943 if (!$objectidnext && count($object->lines) > 0) {
6944 unset($params['attr']['title']);
6945 print dolGetButtonAction($langs->trans('ChangeIntoRepeatableInvoice'), '', 'default', DOL_URL_ROOT.'/compta/facture/card-rec.php?facid='.$object->id.'&action=create', '', true, $params);
6946 }
6947 }
6948
6949 // Clone
6950 if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA) && $usercancreate) {
6951 unset($params['attr']['title']);
6952 print dolGetButtonAction($langs->trans('ToClone'), '', 'clone', $_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=clone&object=invoice&token='.newToken(), '', true, $params);
6953 }
6954
6955 // Remove situation from cycle
6956 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
6957 && $object->isSituationInvoice()
6958 && $usercancreate
6959 && !$objectidnext
6960 && $object->situation_counter > 1
6961 && $object->is_last_in_cycle()
6962 && $usercanunvalidate
6963 ) {
6964 if (($object->total_ttc - $totalcreditnotes) == 0) {
6965 print '<a id="butSituationOut" class="butAction" href="'.$_SERVER['PHP_SELF'].'?facid='.$object->id.'&action=situationout">'.$langs->trans("RemoveSituationFromCycle").'</a>';
6966 } else {
6967 print '<a id="butSituationOutRefused" class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("DisabledBecauseNotEnouthCreditNote").'" >'.$langs->trans("RemoveSituationFromCycle").'</a>';
6968 }
6969 }
6970
6971 // Delete
6972 $isErasable = $object->is_erasable();
6973
6974 $htmltooltip = '';
6975 if ($isErasable == -6) {
6976 $htmltooltip = $langs->trans('DisabledBecauseAlreadySentByEmail');
6977 } elseif ($isErasable == -5) {
6978 $htmltooltip = $langs->trans('DisabledBecauseAlreadyPrintedOnce');
6979 } elseif ($isErasable == -4) {
6980 $htmltooltip = $langs->trans('DisabledBecausePayments');
6981 } elseif ($isErasable == -3) {
6982 $htmltooltip = $langs->trans('DisabledBecauseNotLastSituationInvoice');
6983 } elseif ($isErasable == -2) {
6984 $htmltooltip = $langs->trans('DisabledBecauseNotLastInvoice');
6985 } elseif ($isErasable == -1) {
6986 $htmltooltip = $langs->trans('DisabledBecauseDispatchedInBookkeeping');
6987 } elseif ($isErasable <= 0) { // Any other cases
6988 $htmltooltip = $langs->trans('DisabledBecauseNotErasable').(empty($object->error) ? ': UnknownReason' : ': '.$object->error);
6989 } elseif ($objectidnext) {
6990 $htmltooltip = $langs->trans('DisabledBecauseReplacedInvoice');
6991 }
6992 if ($usercandelete || ($usercancreate && $isErasable == 1)) { // isErasable = 1 means draft with temporary ref (draft can always be deleted with no need of permissions)
6993 $enableDelete = false;
6994 $deleteHref = '#';
6995 if ($isErasable > 0 && ! $objectidnext) {
6996 $deleteHref = $_SERVER["PHP_SELF"].'?facid='.$object->id.'&action=delete&token='.newToken();
6997 $enableDelete = true;
6998 }
6999 unset($params['attr']['title']);
7000 print dolGetButtonAction($htmltooltip, $langs->trans('Delete'), 'delete', $deleteHref, '', $enableDelete, $params);
7001 } else {
7002 unset($params['attr']['title']);
7003 print dolGetButtonAction($htmltooltip, $langs->trans('Delete'), 'delete', '#', '', false);
7004 }
7005 }
7006 print '</div>';
7007 }
7008
7009 // Select mail models is same action as presend
7010 if (GETPOST('modelselected', 'alpha')) {
7011 $action = 'presend';
7012 }
7013 if ($action != 'prerelance' && $action != 'presend') {
7014 print '<div class="fichecenter"><div class="fichehalfleft">';
7015 print '<a name="builddoc"></a>'; // ancre
7016
7017 // Generated documents
7018 $filename = dol_sanitizeFileName($object->ref);
7019 $filedir = $conf->invoice->multidir_output[$object->entity ?? $conf->entity].'/'.dol_sanitizeFileName($object->ref);
7020 $urlsource = $_SERVER['PHP_SELF'].'?facid='.$object->id;
7021 $genallowed = $usercanread;
7022 $delallowed = $usercancreate;
7023 $tooltipAfterComboOfModels = '';
7024 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_INVOICE')) {
7025 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_INVOICE_TERMSOFSALE'));
7026 }
7027
7028 print $formfile->showdocuments(
7029 'facture',
7030 $filename,
7031 $filedir,
7032 $urlsource,
7033 $genallowed,
7034 (int) $delallowed,
7035 $object->model_pdf,
7036 1,
7037 0,
7038 0,
7039 28,
7040 0,
7041 '',
7042 '',
7043 '',
7044 $soc->default_lang,
7045 '',
7046 $object,
7047 0,
7048 'remove_file_comfirm',
7049 $tooltipAfterComboOfModels
7050 );
7051
7052 $somethingshown = $formfile->numoffiles;
7053
7054 // Show links to link elements
7055 $tmparray = $form->showLinkToObjectBlock($object, array(), array('invoice'), 1);
7056 $linktoelem = $tmparray['linktoelem'];
7057 $htmltoenteralink = $tmparray['htmltoenteralink'];
7058 print $htmltoenteralink;
7059
7060 $compatibleImportElementsList = false;
7061 if ($usercancreate
7062 && $object->status == Facture::STATUS_DRAFT
7064 $compatibleImportElementsList = array('commande', 'propal', 'subscription'); // import from linked elements
7065 }
7066 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
7067
7068 // Show online payment link
7069 // The list can be complete by the hook 'doValidatePayment' executed inside getValidOnlinePaymentMethods()
7070 include_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7071 $validpaymentmethod = getValidOnlinePaymentMethods('');
7072 $useonlinepayment = count($validpaymentmethod);
7073
7074 if ($object->status != Facture::STATUS_DRAFT && $useonlinepayment) {
7075 print '<br><!-- Link to pay -->'."\n";
7076 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7077 print showOnlinePaymentUrl('invoice', $object->ref).'<br>';
7078 }
7079
7080 print '</div><div class="fichehalfright">';
7081
7082 $MAXEVENT = 10;
7083
7084 $morehtmlcenter = '<div class="nowraponall">';
7085 $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT.'/compta/facture/messaging.php?id='.$object->id);
7086 $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullList'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/compta/facture/agenda.php?id='.$object->id);
7087 $morehtmlcenter .= '</div>';
7088
7089 // List of actions on element
7090 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
7091 $formactions = new FormActions($db);
7092 $somethingshown = $formactions->showactions($object, 'invoice', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
7093
7094 print '</div></div>';
7095 }
7096
7097
7098 // Presend form
7099 $modelmail = 'facture_send';
7100 $defaulttopic = 'SendBillRef';
7101 $diroutput = $conf->invoice->multidir_output[$object->entity ?? $conf->entity];
7102 $trackid = 'inv'.$object->id;
7103
7104 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
7105}
7106
7107// End of page
7108llxFooter();
7109$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...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
GETPOSTDATE($prefix, $hourTime='', $gm='auto', $saverestore='')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
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.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
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).
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.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
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.
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.
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.
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...
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
buildzip.php
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.