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