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