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