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