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