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