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