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