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