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