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