dolibarr 21.0.0-alpha
card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-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) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
15 * Copyright (C) 2014-2024 Ferran Marcet <fmarcet@2byte.es>
16 * Copyright (C) 2015-2016 Marcos García <marcosgdf@gmail.com>
17 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
18 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19 * Copyright (C) 2023 Nick Fragoulis
20 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 3 of the License, or
26 * (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <https://www.gnu.org/licenses/>.
35 */
36
43// Libraries
44require '../../main.inc.php';
45require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
46require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
47require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
48require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
49require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
50require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
51require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
52require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
53require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
54require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
55require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php';
56require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
57require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
58if (isModEnabled('order')) {
59 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
60}
61if (isModEnabled('project')) {
62 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
63 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
64}
65require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
66
67if (isModEnabled('variants')) {
68 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
69}
70if (isModEnabled('accounting')) {
71 require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
72}
73
74// Load translation files required by the page
75$langs->loadLangs(array('bills', 'companies', 'compta', 'products', 'banks', 'main', 'withdrawals'));
76if (isModEnabled('incoterm')) {
77 $langs->load('incoterm');
78}
79if (isModEnabled('margin')) {
80 $langs->load('margins');
81}
82
83// General $Variables
84$id = (GETPOSTINT('id') ? GETPOSTINT('id') : GETPOSTINT('facid')); // For backward compatibility
85$ref = GETPOST('ref', 'alpha');
86$socid = GETPOSTINT('socid');
87$action = GETPOST('action', 'aZ09');
88$confirm = GETPOST('confirm', 'alpha');
89$cancel = GETPOST('cancel', 'alpha');
90$backtopage = GETPOST('backtopage', 'alpha');
91$lineid = GETPOSTINT('lineid');
92$userid = GETPOSTINT('userid');
93$search_ref = GETPOST('sf_ref', 'alpha') ? GETPOST('sf_ref', 'alpha') : GETPOST('search_ref', 'alpha');
94$search_societe = GETPOST('search_societe', 'alpha');
95$search_montant_ht = GETPOST('search_montant_ht', 'alpha');
96$search_montant_ttc = GETPOST('search_montant_ttc', 'alpha');
97$origin = GETPOST('origin', 'alpha');
98$originid = (GETPOSTINT('originid') ? GETPOSTINT('originid') : GETPOSTINT('origin_id')); // For backward compatibility
99$fac_rec = GETPOSTINT('fac_rec');
100$facid = GETPOSTINT('facid');
101$ref_client = GETPOSTINT('ref_client');
102$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
103$projectid = (GETPOSTINT('projectid') ? GETPOSTINT('projectid') : 0);
104$selectedLines = GETPOST('toselect', 'array');
105
106// PDF
107$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
108$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
109$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
110
111// Number of lines for predefined product/service choices
112$NBLINES = 4;
113
114$usehm = getDolGlobalInt('MAIN_USE_HOURMIN_IN_DATE_RANGE');
115
116$object = new Facture($db);
117$extrafields = new ExtraFields($db);
118
119// Fetch optionals attributes and labels
120$extrafields->fetch_name_optionals_label($object->table_element);
121
122// Load object
123if ($id > 0 || !empty($ref)) {
124 if ($action != 'add') {
125 if (!getDolGlobalString('INVOICE_USE_SITUATION')) {
126 $fetch_situation = false;
127 } else {
128 $fetch_situation = true;
129 }
130 $ret = $object->fetch($id, $ref, '', 0, $fetch_situation);
131 if ($ret > 0 && isset($object->fk_project)) {
132 $ret = $object->fetch_project();
133 }
134 }
135}
136
137// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
138$hookmanager->initHooks(array('invoicecard', 'globalcard'));
139
140// Permissions
141$usercanread = $user->hasRight("facture", "lire");
142$usercancreate = $user->hasRight("facture", "creer");
143$usercanissuepayment = $user->hasRight("facture", "paiement");
144$usercandelete = $user->hasRight("facture", "supprimer") || ($usercancreate && isset($object->status) && $object->status == $object::STATUS_DRAFT);
145$usercancreatecontract = $user->hasRight("contrat", "creer");
146
147// Advanced Permissions
148$usercanvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'validate')));
149$usercansend = (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'send')));
150$usercanreopen = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'reopen')));
151if (getDolGlobalString('INVOICE_DISALLOW_REOPEN')) {
152 $usercanreopen = false;
153}
154$usercanunvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !empty($usercancreate)) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('facture', 'invoice_advance', 'unvalidate')));
155
156$usermustrespectpricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
157$usercancreatemargin = $user->hasRight('margins', 'creer');
158$usercanreadallmargin = $user->hasRight('margins', 'liretous');
159$usercancreatewithdrarequest = $user->hasRight('prelevement', 'bons', 'creer');
160
161$permissionnote = $usercancreate; // Used by the include of actions_setnotes.inc.php
162$permissiondellink = $usercancreate; // Used by the include of actions_dellink.inc.php
163$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdonw.inc.php
164$permissiontoadd = $usercancreate; // Used by the include of actions_addupdatedelete.inc.php
165
166// retained warranty invoice available type
167$retainedWarrantyInvoiceAvailableType = array();
168if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
169 $retainedWarrantyInvoiceAvailableType = explode('+', getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY'));
170}
171
172// Security check
173if ($user->socid) {
174 $socid = $user->socid;
175}
176$isdraft = (($object->status == Facture::STATUS_DRAFT) ? 1 : 0);
177
178$result = restrictedArea($user, 'facture', $object->id, '', '', 'fk_soc', 'rowid', $isdraft);
179
180
181/*
182 * Actions
183 */
184
185$parameters = array('socid' => $socid);
186$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
187if ($reshook < 0) {
188 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
189}
190
191if (empty($reshook)) {
192 $backurlforlist = DOL_URL_ROOT.'/compta/facture/list.php';
193
194 if (empty($backtopage) || ($cancel && empty($id))) {
195 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
196 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
197 $backtopage = $backurlforlist;
198 } else {
199 $backtopage = DOL_URL_ROOT.'/compta/facture/card.php?id='.((!empty($id) && $id > 0) ? $id : '__ID__');
200 }
201 }
202 }
203
204 if ($cancel) {
205 if (!empty($backtopageforcancel)) {
206 header("Location: ".$backtopageforcancel);
207 exit;
208 } elseif (!empty($backtopage)) {
209 header("Location: ".$backtopage);
210 exit;
211 }
212 $action = '';
213 }
214
215 include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be 'include', not 'include_once'
216
217 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
218
219 include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
220
221 // Action clone object
222 if ($action == 'confirm_clone' && $confirm == 'yes' && $permissiontoadd) {
223 if (!($socid > 0)) {
224 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('IdThirdParty')), null, 'errors');
225 } else {
226 $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.
227 '@phan-var-force Facture $objectutil';
228
229 $objectutil->date = dol_mktime(12, 0, 0, GETPOSTINT('newdatemonth'), GETPOSTINT('newdateday'), GETPOSTINT('newdateyear'));
230 $objectutil->socid = $socid;
231 $result = $objectutil->createFromClone($user, $id);
232 if ($result > 0) {
233 $warningMsgLineList = array();
234 // check all product lines are to sell otherwise add a warning message for each product line is not to sell
235 foreach ($objectutil->lines as $line) {
236 if (!is_object($line->product)) {
237 $line->fetch_product();
238 }
239 if (is_object($line->product) && $line->product->id > 0) {
240 if (empty($line->product->status)) {
241 $warningMsgLineList[$line->id] = $langs->trans('WarningLineProductNotToSell', $line->product->ref);
242 }
243 }
244 }
245 if (!empty($warningMsgLineList)) {
246 setEventMessages('', $warningMsgLineList, 'warnings');
247 }
248
249 header("Location: " . $_SERVER['PHP_SELF'] . '?facid=' . $result);
250 exit();
251 } else {
252 $langs->load("errors");
253 setEventMessages($objectutil->error, $objectutil->errors, 'errors');
254 $action = '';
255 }
256 }
257 } elseif ($action == 'reopen' && $usercanreopen) {
258 $result = $object->fetch($id);
259
260 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
261 $result = $object->setUnpaid($user);
262 if ($result > 0) {
263 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
264 exit();
265 } else {
266 setEventMessages($object->error, $object->errors, 'errors');
267 }
268 }
269 } elseif ($action == 'confirm_delete' && $confirm == 'yes') {
270 // Delete invoice
271 $result = $object->fetch($id);
272 $object->fetch_thirdparty();
273
274 $idwarehouse = GETPOST('idwarehouse');
275
276 $qualified_for_stock_change = 0;
277 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
278 $qualified_for_stock_change = $object->hasProductsOrServices(2);
279 } else {
280 $qualified_for_stock_change = $object->hasProductsOrServices(1);
281 }
282
283 $isErasable = $object->is_erasable();
284
285 if (($usercandelete && $isErasable > 0)
286 || ($usercancreate && $isErasable == 1)) {
287 $result = $object->delete($user, 0, $idwarehouse);
288 if ($result > 0) {
289 header('Location: '.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1');
290 exit();
291 } else {
292 setEventMessages($object->error, $object->errors, 'errors');
293 $action = '';
294 }
295 }
296 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
297 // Delete line
298 $object->fetch($id);
299 $object->fetch_thirdparty();
300
301 $result = $object->deleteLine(GETPOSTINT('lineid'));
302 if ($result > 0) {
303 // reorder lines
304 $object->line_order(true);
305 // Define output language
306 $outputlangs = $langs;
307 $newlang = '';
308 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id')) {
309 $newlang = GETPOST('lang_id');
310 }
311 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
312 $newlang = $object->thirdparty->default_lang;
313 }
314 if (!empty($newlang)) {
315 $outputlangs = new Translate("", $conf);
316 $outputlangs->setDefaultLang($newlang);
317 $outputlangs->load('products');
318 }
319 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
320 $ret = $object->fetch($id); // Reload to get new records
321 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
322 }
323 if ($result >= 0) {
324 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
325 exit();
326 }
327 } else {
328 setEventMessages($object->error, $object->errors, 'errors');
329 $action = '';
330 }
331 } elseif ($action == 'unlinkdiscount' && $usercancreate) {
332 // Delete link of credit note to invoice
333 $discount = new DiscountAbsolute($db);
334 $result = $discount->fetch(GETPOSTINT("discountid"));
335 $discount->unlink_invoice();
336 } elseif ($action == 'valid' && $usercancreate) {
337 // Validation
338 $object->fetch($id);
339
340 if ((preg_match('/^[\‍(]?PROV/i', $object->ref) || empty($object->ref)) && // empty should not happened, but when it occurs, the test save life
341 getDolGlobalString('FAC_FORCE_DATE_VALIDATION') // If option enabled, we force invoice date
342 ) {
343 $object->date = dol_now();
344 }
345
346 if (getDolGlobalString('INVOICE_CHECK_POSTERIOR_DATE')) {
347 $last_of_type = $object->willBeLastOfSameType(true);
348 if (empty($object->date_validation) && !$last_of_type[0]) {
349 setEventMessages($langs->transnoentities("ErrorInvoiceIsNotLastOfSameType", $object->ref, dol_print_date($object->date, 'day'), dol_print_date($last_of_type[1], 'day')), null, 'errors');
350 $action = '';
351 }
352 }
353
354 // We check invoice sign
355 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
356 // If a credit note, the sign must be negative
357 if ($object->total_ht > 0) {
358 setEventMessages($langs->trans("ErrorInvoiceAvoirMustBeNegative"), null, 'errors');
359 $action = '';
360 }
361 } else {
362 // If not a credit note, amount with tax must be positive or nul.
363 // Note that amount excluding tax can be negative because you can have a invoice of 100 with vat of 20 that
364 // consumes a credit note of 100 with vat 0 (total with tax is 0 but without tax is -20).
365 // For some cases, credit notes can have a vat of 0 (for example when selling goods in France).
366 if (!getDolGlobalString('FACTURE_ENABLE_NEGATIVE') && $object->total_ttc < 0) {
367 setEventMessages($langs->trans("ErrorInvoiceOfThisTypeMustBePositive"), null, 'errors');
368 $action = '';
369 }
370
371 // Also negative lines should not be allowed on 'non Credit notes' invoices. A test is done when adding or updating lines but we must
372 // do it again in validation to avoid cases where invoice is created from another object that allow negative lines.
373 // 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
374 // when converted into 'available credit' and we will get a positive available credit line.
375 // 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).
376 $array_of_total_ht_per_vat_rate = array();
377 $array_of_total_ht_devise_per_vat_rate = array();
378 foreach ($object->lines as $line) {
379 //$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.
380 $vat_src_code_for_line = '';
381 if (empty($array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
382 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
383 }
384 if (empty($array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line])) {
385 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] = 0;
386 }
387 $array_of_total_ht_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->total_ht;
388 $array_of_total_ht_devise_per_vat_rate[$line->tva_tx.'_'.$vat_src_code_for_line] += $line->multicurrency_total_ht;
389 }
390
391 //var_dump($array_of_total_ht_per_vat_rate);exit;
392 foreach ($array_of_total_ht_per_vat_rate as $vatrate => $tmpvalue) {
393 $tmp_total_ht = price2num($array_of_total_ht_per_vat_rate[$vatrate]);
394 $tmp_total_ht_devise = price2num($array_of_total_ht_devise_per_vat_rate[$vatrate]);
395
396 if (($tmp_total_ht < 0 || $tmp_total_ht_devise < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
397 if ($object->type == $object::TYPE_DEPOSIT) {
398 $langs->load("errors");
399 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
400 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
401 $error++;
402 $action = '';
403 } else {
404 $tmpvatratetoshow = explode('_', $vatrate);
405 $tmpvatratetoshow[0] = round((float) $tmpvatratetoshow[0], 2);
406
407 if ($tmpvatratetoshow[0] != 0) {
408 $langs->load("errors");
409 setEventMessages($langs->trans("ErrorLinesCantBeNegativeForOneVATRate", $tmpvatratetoshow[0]), null, 'errors');
410 $error++;
411 $action = '';
412 }
413 }
414 }
415 }
416 }
417 } elseif ($action == 'classin' && $usercancreate) {
418 $object->fetch($id);
419 $object->setProject(GETPOSTINT('projectid'));
420 } elseif ($action == 'setmode' && $usercancreate) {
421 $object->fetch($id);
422 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
423 if ($result < 0) {
424 dol_print_error($db, $object->error);
425 }
426 } elseif ($action == 'setretainedwarrantyconditions' && $user->hasRight('facture', 'creer')) {
427 $object->fetch($id);
428 $object->retained_warranty_fk_cond_reglement = 0; // To clean property
429 $result = $object->setRetainedWarrantyPaymentTerms(GETPOSTINT('retained_warranty_fk_cond_reglement'));
430 if ($result < 0) {
431 dol_print_error($db, $object->error);
432 }
433
434 $old_rw_date_lim_reglement = $object->retained_warranty_date_limit;
435 $new_rw_date_lim_reglement = $object->calculate_date_lim_reglement($object->retained_warranty_fk_cond_reglement);
436 if ($new_rw_date_lim_reglement > $old_rw_date_lim_reglement) {
437 $object->retained_warranty_date_limit = $new_rw_date_lim_reglement;
438 }
439 if ($object->retained_warranty_date_limit < $object->date) {
440 $object->retained_warranty_date_limit = $object->date;
441 }
442 $result = $object->update($user);
443 if ($result < 0) {
444 dol_print_error($db, $object->error);
445 }
446 } elseif ($action == 'setretainedwarranty' && $user->hasRight('facture', 'creer')) {
447 $object->fetch($id);
448 $result = $object->setRetainedWarranty(GETPOSTFLOAT('retained_warranty'));
449 if ($result < 0) {
450 dol_print_error($db, $object->error);
451 }
452 } elseif ($action == 'setretainedwarrantydatelimit' && $user->hasRight('facture', 'creer')) {
453 $object->fetch($id);
454 $result = $object->setRetainedWarrantyDateLimit(GETPOSTFLOAT('retained_warranty_date_limit'));
455 if ($result < 0) {
456 dol_print_error($db, $object->error);
457 }
458 } elseif ($action == 'setmulticurrencycode' && $usercancreate) { // Multicurrency Code
459 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
460 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) { // Multicurrency rate
461 $result = $object->setMulticurrencyRate(price2num(GETPOST('multicurrency_tx')), GETPOSTINT('calculation_mode'));
462 } elseif ($action == 'setinvoicedate' && $usercancreate) {
463 $object->fetch($id);
464 $old_date_lim_reglement = $object->date_lim_reglement;
465 $newdate = dol_mktime(0, 0, 0, GETPOSTINT('invoicedatemonth'), GETPOSTINT('invoicedateday'), GETPOSTINT('invoicedateyear'), 'tzserver');
466 if (empty($newdate)) {
467 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
468 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id.'&action=editinvoicedate&token='.newToken());
469 exit;
470 }
471 if ($newdate > (dol_now('tzuserrel') + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
472 if (!getDolGlobalString('INVOICE_MAX_FUTURE_DELAY')) {
473 setEventMessages($langs->trans("WarningInvoiceDateInFuture"), null, 'warnings');
474 } else {
475 setEventMessages($langs->trans("WarningInvoiceDateTooFarInFuture"), null, 'warnings');
476 }
477 }
478
479 $object->date = $newdate;
480 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
481 if ($new_date_lim_reglement) {
482 $object->date_lim_reglement = $new_date_lim_reglement;
483 }
484 if ($object->date_lim_reglement < $object->date) {
485 $object->date_lim_reglement = $object->date;
486 }
487 $result = $object->update($user);
488 if ($result < 0) {
489 setEventMessages($object->error, $object->errors, 'errors');
490 $action = 'editinvoicedate';
491 }
492 } elseif ($action == 'setdate_pointoftax' && $usercancreate) {
493 $object->fetch($id);
494
495 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
496
497 $object->date_pointoftax = $date_pointoftax;
498 $result = $object->update($user);
499 if ($result < 0) {
500 dol_print_error($db, $object->error);
501 }
502 } elseif ($action == 'setconditions' && $usercancreate) {
503 $object->fetch($id);
504 $object->cond_reglement_code = 0; // To clean property
505 $object->cond_reglement_id = 0; // To clean property
506
507 $error = 0;
508
509 $db->begin();
510
511 if (!$error) {
512 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'));
513 if ($result < 0) {
514 $error++;
515 setEventMessages($object->error, $object->errors, 'errors');
516 }
517 }
518
519 if (!$error) {
520 $old_date_lim_reglement = $object->date_lim_reglement;
521 $new_date_lim_reglement = $object->calculate_date_lim_reglement();
522 if ($new_date_lim_reglement) {
523 $object->date_lim_reglement = $new_date_lim_reglement;
524 }
525 if ($object->date_lim_reglement < $object->date) {
526 $object->date_lim_reglement = $object->date;
527 }
528 $result = $object->update($user);
529 if ($result < 0) {
530 $error++;
531 setEventMessages($object->error, $object->errors, 'errors');
532 }
533 }
534
535 if ($error) {
536 $db->rollback();
537 } else {
538 $db->commit();
539 }
540 } elseif ($action == 'setpaymentterm' && $usercancreate) {
541 $object->fetch($id);
542 $object->date_lim_reglement = dol_mktime(12, 0, 0, GETPOSTINT('paymenttermmonth'), GETPOSTINT('paymenttermday'), GETPOSTINT('paymenttermyear'));
543 if ($object->date_lim_reglement < $object->date) {
544 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
545 setEventMessages($langs->trans("DatePaymentTermCantBeLowerThanObjectDate"), null, 'warnings');
546 }
547 $result = $object->update($user);
548 if ($result < 0) {
549 dol_print_error($db, $object->error);
550 }
551 } elseif ($action == 'setrevenuestamp' && $usercancreate) {
552 $object->fetch($id);
553 $object->revenuestamp = (float) price2num(GETPOST('revenuestamp'));
554 $result = $object->update($user);
555 $object->update_price(1);
556 if ($result < 0) {
557 dol_print_error($db, $object->error);
558 } else {
559 // Define output language
560 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
561 $outputlangs = $langs;
562 $newlang = '';
563 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
564 $newlang = GETPOST('lang_id', 'aZ09');
565 }
566 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
567 $newlang = $object->thirdparty->default_lang;
568 }
569 if (!empty($newlang)) {
570 $outputlangs = new Translate("", $conf);
571 $outputlangs->setDefaultLang($newlang);
572 $outputlangs->load('products');
573 }
574 $model = $object->model_pdf;
575 $ret = $object->fetch($id); // Reload to get new records
576
577 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
578 if ($result < 0) {
579 setEventMessages($object->error, $object->errors, 'errors');
580 }
581 }
582 }
583 } elseif ($action == 'set_incoterms' && isModEnabled('incoterm')) { // Set incoterm
584 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOSTINT('location_incoterms'));
585 } elseif ($action == 'setbankaccount' && $usercancreate) { // bank account
586 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
587 } elseif ($action == 'setremisepercent' && $usercancreate) {
588 $object->fetch($id);
589 $result = $object->setDiscount($user, price2num(GETPOST('remise_percent'), '', 2));
590 } elseif ($action == "setabsolutediscount" && $usercancreate) {
591 // We have POST[remise_id] or POST[remise_id_for_payment]
592 $db->begin();
593
594 // We use the credit to reduce amount of invoice
595 if (GETPOSTINT("remise_id") > 0) {
596 $ret = $object->fetch($id);
597 if ($ret > 0) {
598 $result = $object->insert_discount(GETPOSTINT("remise_id"));
599 if ($result < 0) {
600 setEventMessages($object->error, $object->errors, 'errors');
601 }
602 } else {
603 $error++;
604 setEventMessages($object->error, $object->errors, 'errors');
605 }
606 }
607 // We use the credit to reduce remain to pay
608 if (GETPOSTINT("remise_id_for_payment") > 0) {
609 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
610 $discount = new DiscountAbsolute($db);
611 $discount->fetch(GETPOSTINT("remise_id_for_payment"));
612
613 //var_dump($object->getRemainToPay(0));
614 //var_dump($discount->amount_ttc);exit;
615 $remaintopay = $object->getRemainToPay(0);
616 if (price2num($discount->amount_ttc) > price2num($remaintopay)) {
617 // TODO Split the discount in 2 automatically
618 $error++;
619 setEventMessages($langs->trans("ErrorDiscountLargerThanRemainToPaySplitItBefore"), null, 'errors');
620 }
621
622 if (!$error) {
623 $result = $discount->link_to_invoice(0, $id);
624 if ($result < 0) {
625 $error++;
626 setEventMessages($discount->error, $discount->errors, 'errors');
627 }
628 }
629
630 if (!$error) {
631 $newremaintopay = $object->getRemainToPay(0);
632 if ($newremaintopay == 0) {
633 $object->setPaid($user);
634 }
635 }
636 }
637
638 if (!$error) {
639 $db->commit();
640 } else {
641 $db->rollback();
642 }
643
644 if (empty($error) && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
645 $outputlangs = $langs;
646 $newlang = '';
647 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
648 $newlang = GETPOST('lang_id', 'aZ09');
649 }
650 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
651 $object->fetch_thirdparty();
652 $newlang = $object->thirdparty->default_lang;
653 }
654 if (!empty($newlang)) {
655 $outputlangs = new Translate("", $conf);
656 $outputlangs->setDefaultLang($newlang);
657 }
658 $ret = $object->fetch($id); // Reload to get new records
659
660 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
661 if ($result < 0) {
662 setEventMessages($object->error, $object->errors, 'errors');
663 }
664 }
665 } elseif ($action == 'setref' && $usercancreate) {
666 $object->fetch($id);
667 $object->setValueFrom('ref', GETPOST('ref'), '', 0, '', '', $user, 'BILL_MODIFY');
668 } elseif ($action == 'setref_client' && $usercancreate) {
669 $object->fetch($id);
670 $object->set_ref_client(GETPOST('ref_client'));
671 } elseif ($action == 'confirm_valid' && $confirm == 'yes' && $usercanvalidate) {
672 // Classify to validated
673 $idwarehouse = GETPOSTINT('idwarehouse');
674
675 $object->fetch($id);
676 $object->fetch_thirdparty();
677
678 // Check for warehouse
679 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
680 $qualified_for_stock_change = 0;
681 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
682 $qualified_for_stock_change = $object->hasProductsOrServices(2);
683 } else {
684 $qualified_for_stock_change = $object->hasProductsOrServices(1);
685 }
686
687 if ($qualified_for_stock_change) {
688 if (!$idwarehouse || $idwarehouse == - 1) {
689 $error++;
690 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
691 $action = '';
692 }
693 }
694 }
695
696 if (!$error) {
697 $result = $object->validate($user, '', $idwarehouse);
698 if ($result >= 0) {
699 // Define output language
700 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
701 $outputlangs = $langs;
702 $newlang = '';
703 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
704 $newlang = GETPOST('lang_id', 'aZ09');
705 }
706 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
707 $newlang = $object->thirdparty->default_lang;
708 }
709 if (!empty($newlang)) {
710 $outputlangs = new Translate("", $conf);
711 $outputlangs->setDefaultLang($newlang);
712 $outputlangs->load('products');
713 }
714 $model = $object->model_pdf;
715
716 $ret = $object->fetch($id); // Reload to get new records
717
718 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
719 if ($result < 0) {
720 setEventMessages($object->error, $object->errors, 'errors');
721 }
722 }
723 } else {
724 if (count($object->errors)) {
725 setEventMessages(null, $object->errors, 'errors');
726 } else {
727 setEventMessages($object->error, $object->errors, 'errors');
728 }
729 }
730 }
731 } elseif ($action == 'confirm_modif' && $usercanunvalidate) {
732 // Go back to draft status (unvalidate)
733 $idwarehouse = GETPOSTINT('idwarehouse');
734
735 $object->fetch($id);
736 $object->fetch_thirdparty();
737
738 // Check parameters
739 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
740 $qualified_for_stock_change = 0;
741 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
742 $qualified_for_stock_change = $object->hasProductsOrServices(2);
743 } else {
744 $qualified_for_stock_change = $object->hasProductsOrServices(1);
745 }
746
747 if ($qualified_for_stock_change) {
748 if (!$idwarehouse || $idwarehouse == - 1) {
749 $error++;
750 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
751 $action = '';
752 }
753 }
754 }
755
756 if (!$error) {
757 // We check if invoice has payments
758 $totalpaid = 0;
759 $sql = 'SELECT pf.amount';
760 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf';
761 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id);
762
763 $result = $db->query($sql);
764 if ($result) {
765 $i = 0;
766 $num = $db->num_rows($result);
767
768 while ($i < $num) {
769 $objp = $db->fetch_object($result);
770 $totalpaid += $objp->amount;
771 $i++;
772 }
773 } else {
774 dol_print_error($db, '');
775 }
776
777 $resteapayer = $object->total_ttc - $totalpaid;
778
779 // We check that invoice lines are transferred into accountancy
780 $ventilExportCompta = $object->getVentilExportCompta();
781
782 // We check if no payment has been made
783 if ($ventilExportCompta == 0) {
784 if (getDolGlobalString('INVOICE_CAN_BE_EDITED_EVEN_IF_PAYMENT_DONE') || ($resteapayer == $object->total_ttc && empty($object->paye))) {
785 $result = $object->setDraft($user, $idwarehouse);
786 if ($result < 0) {
787 setEventMessages($object->error, $object->errors, 'errors');
788 }
789
790 // Define output language
791 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
792 $outputlangs = $langs;
793 $newlang = '';
794 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
795 $newlang = GETPOST('lang_id', 'aZ09');
796 }
797 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
798 $newlang = $object->thirdparty->default_lang;
799 }
800 if (!empty($newlang)) {
801 $outputlangs = new Translate("", $conf);
802 $outputlangs->setDefaultLang($newlang);
803 $outputlangs->load('products');
804 }
805 $model = $object->model_pdf;
806 $ret = $object->fetch($id); // Reload to get new records
807
808 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
809 }
810 }
811 }
812 }
813 } elseif ($action == 'confirm_paid' && $confirm == 'yes' && $usercanissuepayment) {
814 // Classify "paid"
815 $object->fetch($id);
816 $result = $object->setPaid($user);
817 if ($result < 0) {
818 setEventMessages($object->error, $object->errors, 'errors');
819 }
820 } elseif ($action == 'confirm_paid_partially' && $confirm == 'yes' && $usercanissuepayment) {
821 // Classif "paid partially"
822 $object->fetch($id);
823 $close_code = GETPOST("close_code", 'restricthtml');
824 $close_note = GETPOST("close_note", 'restricthtml');
825 if ($close_code) {
826 $result = $object->setPaid($user, $close_code, $close_note);
827 if ($result < 0) {
828 setEventMessages($object->error, $object->errors, 'errors');
829 }
830 } else {
831 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
832 }
833 } elseif ($action == 'confirm_canceled' && $confirm == 'yes') {
834 // Classify "abandoned"
835 $object->fetch($id);
836 $close_code = GETPOST("close_code", 'restricthtml');
837 $close_note = GETPOST("close_note", 'restricthtml');
838 if ($close_code) {
839 $result = $object->setCanceled($user, $close_code, $close_note);
840 if ($result < 0) {
841 setEventMessages($object->error, $object->errors, 'errors');
842 }
843 } else {
844 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Reason")), null, 'errors');
845 }
846 } elseif ($action == 'confirm_converttoreduc' && $confirm == 'yes' && $usercancreate) {
847 // Convert to reduce
848 $object->fetch($id);
849 $object->fetch_thirdparty();
850 //$object->fetch_lines(); // Already done into fetch
851
852 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
853 $discountcheck = new DiscountAbsolute($db);
854 $result = $discountcheck->fetch(0, $object->id);
855
856 $canconvert = 0;
857 if ($object->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
858 $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)
859 }
860 if (($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_SITUATION) && $object->paye == 0 && empty($discountcheck->id)) {
861 $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)
862 }
863
864 if ($canconvert) {
865 $db->begin();
866
867 $amount_ht = $amount_tva = $amount_ttc = array();
868 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
869
870 // Loop on each vat rate
871 $i = 0;
872 foreach ($object->lines as $line) {
873 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
874 $keyforvatrate = $line->tva_tx.($line->vat_src_code ? ' ('.$line->vat_src_code.')' : '');
875
876 $amount_ht[$keyforvatrate] += $line->total_ht;
877 $amount_tva[$keyforvatrate] += $line->total_tva;
878 $amount_ttc[$keyforvatrate] += $line->total_ttc;
879 $multicurrency_amount_ht[$keyforvatrate] += $line->multicurrency_total_ht;
880 $multicurrency_amount_tva[$keyforvatrate] += $line->multicurrency_total_tva;
881 $multicurrency_amount_ttc[$keyforvatrate] += $line->multicurrency_total_ttc;
882 $i++;
883 }
884 }
885 '@phan-var-force array<string,float> $amount_ht
886 @phan-var-force array<string,float> $amount_tva
887 @phan-var-force array<string,float> $amount_ttc
888 @phan-var-force array<string,float> $multicurrency_amount_ht
889 @phan-var-force array<string,float> $multicurrency_amount_tva
890 @phan-var-force array<string,float> $multicurrency_amount_ttc';
891
892 // If some payments were already done, we change the amount to pay using same prorate
893 if (getDolGlobalString('INVOICE_ALLOW_REUSE_OF_CREDIT_WHEN_PARTIALLY_REFUNDED') && $object->type == Facture::TYPE_CREDIT_NOTE) {
894 $alreadypaid = $object->getSommePaiement(); // This can be not 0 if we allow to create credit to reuse from credit notes partially refunded.
895 if ($alreadypaid && abs($alreadypaid) < abs($object->total_ttc)) {
896 $ratio = abs(($object->total_ttc - $alreadypaid) / $object->total_ttc);
897 foreach ($amount_ht as $vatrate => $val) {
898 $amount_ht[$vatrate] = price2num($amount_ht[$vatrate] * $ratio, 'MU');
899 $amount_tva[$vatrate] = price2num($amount_tva[$vatrate] * $ratio, 'MU');
900 $amount_ttc[$vatrate] = price2num($amount_ttc[$vatrate] * $ratio, 'MU');
901 $multicurrency_amount_ht[$vatrate] = price2num($multicurrency_amount_ht[$vatrate] * $ratio, 'MU');
902 $multicurrency_amount_tva[$vatrate] = price2num($multicurrency_amount_tva[$vatrate] * $ratio, 'MU');
903 $multicurrency_amount_ttc[$vatrate] = price2num($multicurrency_amount_ttc[$vatrate] * $ratio, 'MU');
904 }
905 }
906 }
907 //var_dump($amount_ht);var_dump($amount_tva);var_dump($amount_ttc);exit;
908
909 // Insert one discount by VAT rate category
910 $discount = new DiscountAbsolute($db);
911 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
912 $discount->description = '(CREDIT_NOTE)';
913 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
914 $discount->description = '(DEPOSIT)';
916 $discount->description = '(EXCESS RECEIVED)';
917 } else {
918 setEventMessages($langs->trans('CantConvertToReducAnInvoiceOfThisType'), null, 'errors');
919 }
920 $discount->fk_soc = $object->socid;
921 $discount->socid = $object->socid;
922 $discount->fk_facture_source = $object->id;
923
924 $error = 0;
925
927 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
928
929 // Total payments
930 $sql = 'SELECT SUM(pf.amount) as total_paiements';
931 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
932 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
933 $sql .= ' WHERE pf.fk_facture = '.((int) $object->id);
934 $sql .= ' AND pf.fk_paiement = p.rowid';
935 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
936 $resql = $db->query($sql);
937 if (!$resql) {
938 dol_print_error($db);
939 }
940
941 $res = $db->fetch_object($resql);
942 $total_paiements = $res->total_paiements;
943
944 // Total credit note and deposit
945 $total_creditnote_and_deposit = 0;
946 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
947 $sql .= " re.description, re.fk_facture_source";
948 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
949 $sql .= " WHERE fk_facture = ".((int) $object->id);
950 $resql = $db->query($sql);
951 if (!empty($resql)) {
952 while ($obj = $db->fetch_object($resql)) {
953 $total_creditnote_and_deposit += $obj->amount_ttc;
954 }
955 } else {
956 dol_print_error($db);
957 }
958
959 $discount->amount_ht = $discount->amount_ttc = $total_paiements + $total_creditnote_and_deposit - $object->total_ttc;
960 $discount->amount_tva = 0;
961 $discount->tva_tx = 0;
962 $discount->vat_src_code = '';
963
964 $result = $discount->create($user);
965 if ($result < 0) {
966 $error++;
967 }
968 }
970 foreach ($amount_ht as $tva_tx => $xxx) {
971 $discount->amount_ht = abs((float) $amount_ht[$tva_tx]);
972 $discount->amount_tva = abs((float) $amount_tva[$tva_tx]);
973 $discount->amount_ttc = abs((float) $amount_ttc[$tva_tx]);
974 $discount->multicurrency_amount_ht = abs((float) $multicurrency_amount_ht[$tva_tx]);
975 $discount->multicurrency_amount_tva = abs((float) $multicurrency_amount_tva[$tva_tx]);
976 $discount->multicurrency_amount_ttc = abs((float) $multicurrency_amount_ttc[$tva_tx]);
977
978 // Clean vat code
979 $reg = array();
980 $vat_src_code = '';
981 if (preg_match('/\‍((.*)\‍)/', $tva_tx, $reg)) {
982 $vat_src_code = $reg[1];
983 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx); // Remove code into vatrate.
984 }
985
986 $discount->tva_tx = abs((float) $tva_tx);
987 $discount->vat_src_code = $vat_src_code;
988
989 $result = $discount->create($user);
990 if ($result < 0) {
991 $error++;
992 break;
993 }
994 }
995 }
996
997 if (empty($error)) {
998 if ($object->type != Facture::TYPE_DEPOSIT) {
999 // Set invoice as paid
1000 $result = $object->setPaid($user);
1001 if ($result >= 0) {
1002 $db->commit();
1003 } else {
1004 setEventMessages($object->error, $object->errors, 'errors');
1005 $db->rollback();
1006 }
1007 } else {
1008 $db->commit();
1009 }
1010 } else {
1011 setEventMessages($discount->error, $discount->errors, 'errors');
1012 $db->rollback();
1013 }
1014 }
1015 } elseif ($action == 'confirm_delete_paiement' && $confirm == 'yes' && $usercanissuepayment) {
1016 // Delete payment
1017 $object->fetch($id);
1018 if ($object->status == Facture::STATUS_VALIDATED && $object->paye == 0) {
1019 $paiement = new Paiement($db);
1020 $result = $paiement->fetch(GETPOSTINT('paiement_id'));
1021 if ($result > 0) {
1022 $result = $paiement->delete($user); // If fetch ok and found
1023 if ($result >= 0) {
1024 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
1025 exit;
1026 }
1027 }
1028 if ($result < 0) {
1029 setEventMessages($paiement->error, $paiement->errors, 'errors');
1030 }
1031 }
1032 } elseif ($action == 'add' && $usercancreate) {
1033 // Insert new invoice in database
1034 if ($socid > 0) {
1035 $object->socid = GETPOSTINT('socid');
1036 }
1037
1038 if (GETPOSTINT('type') === '') {
1039 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1040 }
1041
1042 $db->begin();
1043
1044 $error = 0;
1045 $originentity = GETPOSTINT('originentity');
1046 // Fill array 'array_options' with data from add form
1047 $ret = $extrafields->setOptionalsFromPost(null, $object);
1048 if ($ret < 0) {
1049 $error++;
1050 }
1051
1052 $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
1053 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
1054
1055 // Replacement invoice
1056 if (GETPOST('type') == Facture::TYPE_REPLACEMENT) {
1057 if (empty($dateinvoice)) {
1058 $error++;
1059 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1060 $action = 'create';
1061 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1062 $error++;
1063 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1064 $action = 'create';
1065 }
1066
1067 if (!(GETPOSTINT('fac_replacement') > 0)) {
1068 $error++;
1069 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ReplaceInvoice")), null, 'errors');
1070 $action = 'create';
1071 }
1072
1073 if (!$error) {
1074 // This is a replacement invoice
1075 $result = $object->fetch(GETPOSTINT('fac_replacement'));
1076 $object->fetch_thirdparty();
1077
1078 $object->date = $dateinvoice;
1079 $object->date_pointoftax = $date_pointoftax;
1080 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1081 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1082 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1083 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1084 $object->model_pdf = GETPOST('model', 'alphanohtml');
1085 $object->fk_project = GETPOSTINT('projectid');
1086 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
1087 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1088 $object->fk_account = GETPOSTINT('fk_account');
1089 //$object->remise_absolue = price2num(GETPOST('remise_absolue'), 'MU', 2);
1090 //$object->remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1091 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1092 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1093 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1094 $object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
1095
1096 // Special properties of replacement invoice
1097 $object->fk_facture_source = GETPOSTINT('fac_replacement');
1099
1100 $id = $object->createFromCurrent($user);
1101 if ($id <= 0) {
1102 setEventMessages($object->error, $object->errors, 'errors');
1103 }
1104 }
1105 }
1106
1107 // Credit note invoice
1108 if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) {
1109 $sourceinvoice = GETPOSTINT('fac_avoir');
1110 if (!($sourceinvoice > 0) && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
1111 $error++;
1112 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CorrectInvoice")), null, 'errors');
1113 $action = 'create';
1114 }
1115
1116 if (empty($dateinvoice)) {
1117 $error++;
1118 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1119 $action = 'create';
1120 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1121 $error++;
1122 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1123 $action = 'create';
1124 }
1125
1126 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1127 $error++;
1128 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1129 $action = 'create';
1130 }
1131
1132 if (!$error) {
1133 if (!empty($originentity)) {
1134 $object->entity = $originentity;
1135 }
1136 $object->socid = GETPOSTINT('socid');
1137 $object->subtype = GETPOSTINT('subtype');
1138 $object->ref = GETPOST('ref');
1139 $object->date = $dateinvoice;
1140 $object->date_pointoftax = $date_pointoftax;
1141 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1142 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1143 $object->ref_client = GETPOST('ref_client', 'alphanohtml');
1144 $object->ref_customer = GETPOST('ref_client', 'alphanohtml');
1145 $object->model_pdf = GETPOST('model');
1146 $object->fk_project = GETPOSTINT('projectid');
1147 $object->cond_reglement_id = 0; // No payment term for a credit note
1148 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1149 $object->fk_account = GETPOSTINT('fk_account');
1150 //$object->remise_absolue = price2num(GETPOST('remise_absolue'), 'MU');
1151 //$object->remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1152 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1153 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1154 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1155 $object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
1156
1157 // Special properties of replacement invoice
1158 $object->fk_facture_source = $sourceinvoice > 0 ? $sourceinvoice : '';
1160
1161 $facture_source = new Facture($db); // fetch origin object
1162 if ($facture_source->fetch($object->fk_facture_source) > 0) {
1163 if ($facture_source->type == Facture::TYPE_SITUATION) {
1164 $object->situation_counter = $facture_source->situation_counter;
1165 $object->situation_cycle_ref = $facture_source->situation_cycle_ref;
1166 $facture_source->fetchPreviousNextSituationInvoice();
1167 }
1168 }
1169
1170
1171 $id = $object->create($user);
1172 if ($id < 0) {
1173 $error++;
1174 } else {
1175 // copy internal contacts
1176 if ($object->copy_linked_contact($facture_source, 'internal') < 0) {
1177 $error++;
1178 } elseif ($facture_source->socid == $object->socid) {
1179 // copy external contacts if same company
1180 if ($object->copy_linked_contact($facture_source, 'external') < 0) {
1181 $error++;
1182 }
1183 }
1184 }
1185
1186 // NOTE: Pb with situation invoice
1187 // NOTE: fields total on situation invoice are stored as cumulative values on total of lines (bad) but delta on invoice total
1188 // NOTE: fields total on credit note are stored as delta both on total of lines and on invoice total (good)
1189 // NOTE: fields situation_percent on situation invoice are stored as cumulative values on lines (bad)
1190 // NOTE: fields situation_percent on credit note are stored as delta on lines (good)
1191 if (GETPOSTINT('invoiceAvoirWithLines') == 1 && $id > 0) {
1192 if (!empty($facture_source->lines)) {
1193 $fk_parent_line = 0;
1194
1195 foreach ($facture_source->lines as $line) {
1196 // Extrafields
1197 if (method_exists($line, 'fetch_optionals')) {
1198 // load extrafields
1199 $line->fetch_optionals();
1200 }
1201
1202 // Reset fk_parent_line for no child products and special product
1203 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1204 $fk_parent_line = 0;
1205 }
1206
1207
1208 if ($facture_source->type == Facture::TYPE_SITUATION) {
1209 $source_fk_prev_id = $line->fk_prev_id; // temporary storing situation invoice fk_prev_id
1210 $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
1211
1212 if (!empty($facture_source->tab_previous_situation_invoice)) {
1213 // search the last standard invoice in cycle and the possible credit note between this last and facture_source
1214 // TODO Move this out of loop of $facture_source->lines
1215 $tab_jumped_credit_notes = array();
1216 $lineIndex = count($facture_source->tab_previous_situation_invoice) - 1;
1217 $searchPreviousInvoice = true;
1218 while ($searchPreviousInvoice) {
1219 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_SITUATION || $lineIndex < 1) {
1220 $searchPreviousInvoice = false; // find, exit;
1221 break;
1222 } else {
1223 if ($facture_source->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_CREDIT_NOTE) {
1224 $tab_jumped_credit_notes[$lineIndex] = $facture_source->tab_previous_situation_invoice[$lineIndex]->id;
1225 }
1226 $lineIndex--; // go to previous invoice in cycle
1227 }
1228 }
1229
1230 $maxPrevSituationPercent = 0;
1231 foreach ($facture_source->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
1232 if ($prevLine->id == $source_fk_prev_id) {
1233 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
1234
1235 //$line->subprice = $line->subprice - $prevLine->subprice;
1236 $line->total_ht -= $prevLine->total_ht;
1237 $line->total_tva -= $prevLine->total_tva;
1238 $line->total_ttc -= $prevLine->total_ttc;
1239 $line->total_localtax1 -= $prevLine->total_localtax1;
1240 $line->total_localtax2 -= $prevLine->total_localtax2;
1241
1242 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1243 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1244 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1245 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1246 }
1247 }
1248
1249 // prorata
1250 $line->situation_percent = $maxPrevSituationPercent - $line->situation_percent;
1251
1252 //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>';
1253
1254 // If there is some credit note between last situation invoice and invoice used for credit note generation (note: credit notes are stored as delta)
1255 $maxPrevSituationPercent = 0;
1256 foreach ($tab_jumped_credit_notes as $index => $creditnoteid) {
1257 foreach ($facture_source->tab_previous_situation_invoice[$index]->lines as $prevLine) {
1258 if ($prevLine->fk_prev_id == $source_fk_prev_id) {
1259 $maxPrevSituationPercent = $prevLine->situation_percent;
1260
1261 $line->total_ht -= $prevLine->total_ht;
1262 $line->total_tva -= $prevLine->total_tva;
1263 $line->total_ttc -= $prevLine->total_ttc;
1264 $line->total_localtax1 -= $prevLine->total_localtax1;
1265 $line->total_localtax2 -= $prevLine->total_localtax2;
1266
1267 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
1268 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
1269 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
1270 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
1271 }
1272 }
1273 }
1274
1275 // prorata
1276 $line->situation_percent += $maxPrevSituationPercent;
1277
1278 //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>';
1279 }
1280 }
1281
1282 $line->fk_facture = $object->id;
1283 $line->fk_parent_line = $fk_parent_line;
1284
1285 $line->subprice = -$line->subprice; // invert price for object
1286 // $line->pa_ht = $line->pa_ht; // we chose to have buy/cost price always positive, so no revert of sign here
1287 $line->total_ht = -$line->total_ht;
1288 $line->total_tva = -$line->total_tva;
1289 $line->total_ttc = -$line->total_ttc;
1290 $line->total_localtax1 = -$line->total_localtax1;
1291 $line->total_localtax2 = -$line->total_localtax2;
1292
1293 $line->multicurrency_subprice = -$line->multicurrency_subprice;
1294 $line->multicurrency_total_ht = -$line->multicurrency_total_ht;
1295 $line->multicurrency_total_tva = -$line->multicurrency_total_tva;
1296 $line->multicurrency_total_ttc = -$line->multicurrency_total_ttc;
1297
1298 $line->context['createcreditnotefrominvoice'] = 1;
1299 $result = $line->insert(0, 1); // When creating credit note with same lines than source, we must ignore error if discount already linked
1300
1301 $object->lines[] = $line; // insert new line in current object
1302
1303 // Defined the new fk_parent_line
1304 if ($result > 0 && $line->product_type == 9) {
1305 $fk_parent_line = $result;
1306 }
1307 }
1308
1309 $object->update_price(1);
1310 }
1311 }
1312
1313 if (GETPOSTINT('invoiceAvoirWithPaymentRestAmount') == 1 && $id > 0) {
1314 if ($facture_source->fetch($object->fk_facture_source) > 0) {
1315 $totalpaid = $facture_source->getSommePaiement();
1316 $totalcreditnotes = $facture_source->getSumCreditNotesUsed();
1317 $totaldeposits = $facture_source->getSumDepositsUsed();
1318 $remain_to_pay = abs($facture_source->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits);
1319
1320 if (getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') == 'default') {
1321 if ((empty($object->thirdparty) || !is_object($object->thirdparty) || get_class($object->thirdparty) != 'Societe')) {
1322 $object->fetch_thirdparty();
1323 }
1324 if (!empty($object->thirdparty) && is_object($object->thirdparty) && get_class($object->thirdparty) == 'Societe') {
1325 $tva_tx = get_default_tva($mysoc, $object->thirdparty);
1326 } else {
1327 $tva_tx = 0;
1328 }
1329 } elseif ((float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY') > 0) {
1330 $tva_tx = (float) getDolGlobalString('INVOICE_VAT_TO_USE_ON_CREDIT_NOTE_WHEN_GENERATED_FROM_REMAIN_TO_PAY');
1331 } else {
1332 $tva_tx = 0;
1333 }
1334
1335 $object->addline($langs->trans('invoiceAvoirLineWithPaymentRestAmount'), $remain_to_pay, 1, $tva_tx, 0, 0, 0, 0, '', '', 0, 0, 0, 'TTC');
1336 }
1337 }
1338
1339 // Add link between credit note and origin
1340 if (!empty($object->fk_facture_source) && $id > 0) {
1341 $facture_source->fetch($object->fk_facture_source);
1342 $facture_source->fetchObjectLinked();
1343
1344 if (!empty($facture_source->linkedObjectsIds)) {
1345 foreach ($facture_source->linkedObjectsIds as $sourcetype => $TIds) {
1346 $object->add_object_linked($sourcetype, current($TIds));
1347 }
1348 }
1349 }
1350 }
1351 }
1352
1353 // Standard invoice or Deposit invoice, created from a Predefined template invoice
1354 if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT) && GETPOSTINT('fac_rec') > 0) {
1355 if (empty($dateinvoice)) {
1356 $error++;
1357 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1358 $action = 'create';
1359 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1360 $error++;
1361 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1362 $action = 'create';
1363 }
1364
1365
1366 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1367 $error++;
1368 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1369 $action = 'create';
1370 }
1371
1372 if (!$error) {
1373 $object->socid = GETPOSTINT('socid');
1374 $object->type = GETPOSTINT('type');
1375 $object->subtype = GETPOSTINT('subtype');
1376 $object->ref = GETPOST('ref');
1377 $object->date = $dateinvoice;
1378 $object->date_pointoftax = $date_pointoftax;
1379 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1380 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1381 $object->ref_customer = GETPOST('ref_client');
1382 $object->ref_client = $object->ref_customer;
1383 $object->model_pdf = GETPOST('model');
1384 $object->fk_project = GETPOSTINT('projectid');
1385 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1386 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
1387 $object->fk_account = GETPOSTINT('fk_account');
1388 $object->amount = price2num(GETPOST('amount'));
1389 //$object->remise_absolue = price2num(GETPOST('remise_absolue'), 'MU');
1390 //$object->remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1391 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1392 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1393 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1394 $object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
1395
1396 // Source facture
1397 $object->fac_rec = GETPOSTINT('fac_rec');
1398
1399 $id = $object->create($user); // This include recopy of links from recurring invoice and recurring invoice lines
1400 }
1401 }
1402
1403 // Standard or deposit invoice, not from a Predefined template invoice
1404 if ((GETPOST('type') == Facture::TYPE_STANDARD || GETPOST('type') == Facture::TYPE_DEPOSIT || GETPOST('type') == Facture::TYPE_PROFORMA || (GETPOST('type') == Facture::TYPE_SITUATION && !GETPOST('situations'))) && GETPOST('fac_rec') <= 0) {
1405 $typeamount = GETPOST('typedeposit', 'aZ09');
1406 $valuestandardinvoice = price2num(str_replace('%', '', GETPOST('valuestandardinvoice', 'alpha')), 'MU');
1407 $valuedeposit = price2num(str_replace('%', '', GETPOST('valuedeposit', 'alpha')), 'MU');
1408
1409 if (GETPOSTINT('socid') < 1) {
1410 $error++;
1411 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Customer")), null, 'errors');
1412 $action = 'create';
1413 }
1414
1415 if (empty($dateinvoice)) {
1416 $error++;
1417 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
1418 $action = 'create';
1419 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1420 $error++;
1421 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1422 $action = 'create';
1423 }
1424
1425
1426 if (GETPOST('type') == Facture::TYPE_STANDARD) {
1427 if ($valuestandardinvoice < 0 || $valuestandardinvoice > 100) {
1428 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1429 $error++;
1430 $action = 'create';
1431 }
1432 } elseif (GETPOST('type') == Facture::TYPE_DEPOSIT) {
1433 if ($typeamount && !empty($origin) && !empty($originid)) {
1434 if ($typeamount == 'amount' && $valuedeposit <= 0) {
1435 setEventMessages($langs->trans("ErrorAnAmountWithoutTaxIsRequired"), null, 'errors');
1436 $error++;
1437 $action = 'create';
1438 }
1439 if ($typeamount == 'variable' && $valuedeposit <= 0) {
1440 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1441 $error++;
1442 $action = 'create';
1443 }
1444 if ($typeamount == 'variablealllines' && $valuedeposit <= 0) {
1445 setEventMessages($langs->trans("ErrorAPercentIsRequired"), null, 'errors');
1446 $error++;
1447 $action = 'create';
1448 }
1449 }
1450 }
1451
1452
1453 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED') && empty(GETPOST("subtype"))) {
1454 $error++;
1455 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSubtype")), null, 'errors');
1456 $action = 'create';
1457 }
1458
1459 if (!$error) {
1460 $object->socid = GETPOSTINT('socid');
1461 $object->type = GETPOSTINT('type');
1462 $object->subtype = GETPOSTINT('subtype');
1463 $object->ref = GETPOST('ref');
1464 $object->date = $dateinvoice;
1465 $object->date_pointoftax = $date_pointoftax;
1466 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
1467 $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
1468 $object->ref_client = GETPOST('ref_client');
1469 $object->ref_customer = GETPOST('ref_client');
1470 $object->model_pdf = GETPOST('model');
1471 $object->fk_project = GETPOSTINT('projectid');
1472 $object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
1473 $object->mode_reglement_id = GETPOST('mode_reglement_id');
1474 $object->fk_account = GETPOSTINT('fk_account');
1475 $object->amount = price2num(GETPOST('amount'));
1476 //$object->remise_absolue = price2num(GETPOST('remise_absolue'), 'MU');
1477 //$object->remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1478 $object->fk_incoterms = GETPOSTINT('incoterm_id');
1479 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
1480 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
1481 $object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
1482
1483 if (GETPOST('type') == Facture::TYPE_SITUATION) {
1484 $object->situation_counter = 1;
1485 $object->situation_final = 0;
1486 $object->situation_cycle_ref = $object->newCycle();
1487 }
1488
1489 if (in_array($object->type, $retainedWarrantyInvoiceAvailableType)) {
1490 $object->retained_warranty = GETPOSTINT('retained_warranty');
1491 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
1492 } else {
1493 $object->retained_warranty = 0;
1494 $object->retained_warranty_fk_cond_reglement = 0;
1495 }
1496
1497 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
1498 if (!empty($retained_warranty_date_limit) && dol_stringtotime($retained_warranty_date_limit)) {
1499 $object->retained_warranty_date_limit = dol_stringtotime($retained_warranty_date_limit);
1500 }
1501 $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);
1502
1503 $object->fetch_thirdparty();
1504
1505 // If creation from another object of another module (Example: origin=propal, originid=1)
1506 if (!empty($origin) && !empty($originid)) {
1507 $regs = array();
1508 // Parse element/subelement (ex: project_task)
1509 $element = $subelement = $origin;
1510 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
1511 $element = $regs[1];
1512 $subelement = $regs[2];
1513 }
1514
1515 // For compatibility
1516 if ($element == 'order') {
1517 $element = $subelement = 'commande';
1518 }
1519 if ($element == 'propal') {
1520 $element = 'comm/propal';
1521 $subelement = 'propal';
1522 }
1523 if ($element == 'contract') {
1524 $element = $subelement = 'contrat';
1525 }
1526 if ($element == 'inter') {
1527 $element = $subelement = 'ficheinter';
1528 }
1529 if ($element == 'shipping') {
1530 $element = $subelement = 'expedition';
1531 }
1532
1533 $object->origin = $origin; // deprecated
1534 $object->origin_type = $origin;
1535 $object->origin_id = $originid;
1536
1537 // Possibility to add external linked objects with hooks
1538 $object->linked_objects[$object->origin_type] = $object->origin_id;
1539 // link with order if it is a shipping invoice
1540 if ($object->origin == 'shipping') {
1541 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1542 $exp = new Expedition($db);
1543 $exp->fetch($object->origin_id);
1544 $exp->fetchObjectLinked();
1545 if (is_array($exp->linkedObjectsIds['commande']) && count($exp->linkedObjectsIds['commande']) > 0) {
1546 foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1547 $object->linked_objects['commande'] = $value;
1548 }
1549 }
1550 }
1551
1552 if (GETPOSTISARRAY('other_linked_objects')) {
1553 $object->linked_objects = array_merge($object->linked_objects, GETPOST('other_linked_objects', 'array:int'));
1554 }
1555
1556 $id = $object->create($user); // This include class to add_object_linked() and add add_contact()
1557
1558 if ($id > 0) {
1559 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
1560
1561 $classname = ucfirst($subelement);
1562 $srcobject = new $classname($db);
1563 '@phan-var-force CommonObject $srcobject';
1564
1565 dol_syslog("Try to find source object origin=".$object->origin." originid=".$object->origin_id." to add lines or deposit lines");
1566 $result = $srcobject->fetch($object->origin_id);
1567
1568 // If deposit invoice - down payment with 1 line (fixed amount or percent)
1569 if (GETPOST('type') == Facture::TYPE_DEPOSIT && in_array($typeamount, array('amount', 'variable'))) {
1570 // Define the array $amountdeposit
1571 $amountdeposit = array();
1572 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA')) {
1573 if ($typeamount == 'amount') {
1574 $amount = (float) $valuedeposit;
1575 } else {
1576 $amount = $srcobject->total_ttc * ((float) $valuedeposit / 100);
1577 }
1578
1579 $TTotalByTva = array();
1580 foreach ($srcobject->lines as &$line) {
1581 if (!empty($line->special_code)) {
1582 continue;
1583 }
1584 $TTotalByTva[$line->tva_tx] += $line->total_ttc;
1585 }
1586 '@phan-var-force array<string,float> $TTotalByTva';
1587
1588 $amount_ttc_diff = 0.;
1589 foreach ($TTotalByTva as $tva => &$total) {
1590 $coef = $total / $srcobject->total_ttc; // Calc coef
1591 $am = $amount * $coef;
1592 $amount_ttc_diff += $am;
1593 $amountdeposit[$tva] += $am / (1 + (float) $tva / 100); // Convert into HT for the addline
1594 }
1595 } else {
1596 if ($typeamount == 'amount') {
1597 $amountdeposit[0] = $valuedeposit;
1598 } elseif ($typeamount == 'variable') {
1599 if ($result > 0) {
1600 $totalamount = 0;
1601 $lines = $srcobject->lines;
1602 $numlines = count($lines);
1603 for ($i = 0; $i < $numlines; $i++) {
1604 $qualified = 1;
1605 if (empty($lines[$i]->qty)) {
1606 $qualified = 0; // We discard qty=0, it is an option
1607 }
1608 if (!empty($lines[$i]->special_code)) {
1609 $qualified = 0; // We discard special_code (frais port, ecotaxe, option, ...)
1610 }
1611 if ($qualified) {
1612 $totalamount += $lines[$i]->total_ht; // Fixme : is it not for the customer ? Shouldn't we take total_ttc ?
1613 $tva_tx = $lines[$i]->tva_tx;
1614 $amountdeposit[$tva_tx] += ($lines[$i]->total_ht * (float) $valuedeposit) / 100;
1615 }
1616 }
1617
1618 if ($totalamount == 0) {
1619 $amountdeposit[0] = 0;
1620 }
1621 } else {
1622 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
1623 $error++;
1624 }
1625 }
1626
1627 $amount_ttc_diff = $amountdeposit[0];
1628 }
1629
1630 foreach ($amountdeposit as $tva => $amount) {
1631 if (empty($amount)) {
1632 continue;
1633 }
1634
1635 $arraylist = array(
1636 'amount' => 'FixAmount',
1637 'variable' => 'VarAmount'
1638 );
1639 $descline = '(DEPOSIT)';
1640 //$descline.= ' - '.$langs->trans($arraylist[$typeamount]);
1641 if ($typeamount == 'amount') {
1642 $descline .= ' ('.price($valuedeposit, 0, $langs, 0, - 1, - 1, (!empty($object->multicurrency_code) ? $object->multicurrency_code : $conf->currency)).')';
1643 } elseif ($typeamount == 'variable') {
1644 $descline .= ' ('.$valuedeposit.'%)';
1645 }
1646
1647 $descline .= ' - '.$srcobject->ref;
1648 $result = $object->addline(
1649 $descline,
1650 $amount, // subprice
1651 1, // quantity
1652 $tva, // vat rate
1653 0, // localtax1_tx
1654 0, // localtax2_tx
1655 getDolGlobalInt('INVOICE_PRODUCTID_DEPOSIT'), // fk_product
1656 0, // remise_percent
1657 0, // date_start
1658 0, // date_end
1659 0,
1660 $lines[$i]->info_bits, // info_bits
1661 0,
1662 'HT',
1663 0,
1664 0, // product_type
1665 1,
1666 $lines[$i]->special_code,
1667 $object->origin,
1668 0,
1669 0,
1670 0,
1671 0,
1672 '',
1673 array(), // array_options
1674 100,
1675 0,
1676 null,
1677 0,
1678 '',
1679 (!empty($conf->global->MAIN_DEPOSIT_MULTI_TVA) ? 0 : 1)
1680 );
1681 }
1682
1683 $diff = $object->total_ttc - $amount_ttc_diff;
1684
1685 if (getDolGlobalString('MAIN_DEPOSIT_MULTI_TVA') && $diff != 0) {
1686 $object->fetch_lines();
1687 $subprice_diff = $object->lines[0]->subprice - $diff / (1 + $object->lines[0]->tva_tx / 100);
1688 $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);
1689 }
1690 }
1691
1692 // standard invoice, credit note, or down payment from a percent of all lines
1693 if (GETPOST('type') != Facture::TYPE_DEPOSIT || (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines')) {
1694 if ($result > 0) {
1695 $lines = $srcobject->lines;
1696 if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
1697 $srcobject->fetch_lines();
1698 $lines = $srcobject->lines;
1699 }
1700
1701 // If we create a standard invoice with a percent, we change amount by changing the qty
1702 if (GETPOST('type') == Facture::TYPE_STANDARD && $valuestandardinvoice > 0 && $valuestandardinvoice < 100) {
1703 if (is_array($lines)) {
1704 foreach ($lines as $line) {
1705 // We keep ->subprice and ->pa_ht, but we change the qty
1706 $line->qty = price2num((float) $line->qty * (float) $valuestandardinvoice / 100, 'MS');
1707 }
1708 }
1709 }
1710 // If we create a down payment with a percent on all lines, we change amount by changing the qty
1711 if (GETPOST('type') == Facture::TYPE_DEPOSIT && $typeamount == 'variablealllines') {
1712 if (is_array($lines)) {
1713 foreach ($lines as $line) {
1714 // We keep ->subprice and ->pa_ht, but we change the qty
1715 $line->qty = price2num((float) $line->qty * (float) $valuedeposit / 100, 'MS');
1716 }
1717 }
1718 }
1719
1720 $fk_parent_line = 0;
1721 $num = count($lines);
1722
1723 for ($i = 0; $i < $num; $i++) {
1724 if (!in_array($lines[$i]->id, $selectedLines)) {
1725 continue; // Skip unselected lines
1726 }
1727
1728 // Don't add lines with qty 0 when coming from a shipment including all order lines
1729 if ($srcobject->element == 'shipping' && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS') && $lines[$i]->qty == 0) {
1730 continue;
1731 }
1732 // Don't add closed lines when coming from a contract (Set constant to '0,5' to exclude also inactive lines)
1733 if (!isset($conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE)) {
1734 $conf->global->CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE = '5';
1735 }
1736 if ($srcobject->element == 'contrat' && in_array($lines[$i]->statut, explode(',', getDolGlobalString('CONTRACT_EXCLUDE_SERVICES_STATUS_FOR_INVOICE')))) {
1737 continue;
1738 }
1739
1740 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
1741 $desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : '');
1742
1743 if ($object->situation_counter == 1) {
1744 $lines[$i]->situation_percent = 0;
1745 }
1746
1747 if ($lines[$i]->subprice < 0 && !getDolGlobalString('INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN')) {
1748 // Negative line, we create a discount line
1749 if (empty($desc)) {
1750 $desc = $label ? $label : $langs->trans('Discount');
1751 }
1752
1753 $discount = new DiscountAbsolute($db);
1754 $discount->fk_soc = $object->socid;
1755 $discount->socid = $object->socid;
1756 $discount->amount_ht = abs($lines[$i]->total_ht);
1757 $discount->amount_tva = abs($lines[$i]->total_tva);
1758 $discount->amount_ttc = abs($lines[$i]->total_ttc);
1759 $discount->tva_tx = $lines[$i]->tva_tx;
1760 $discount->fk_user = $user->id;
1761 $discount->description = $desc;
1762 $discount->multicurrency_subprice = abs($lines[$i]->multicurrency_subprice);
1763 $discount->multicurrency_amount_ht = abs($lines[$i]->multicurrency_total_ht);
1764 $discount->multicurrency_amount_tva = abs($lines[$i]->multicurrency_total_tva);
1765 $discount->multicurrency_amount_ttc = abs($lines[$i]->multicurrency_total_ttc);
1766
1767 $discountid = $discount->create($user);
1768 if ($discountid > 0) {
1769 $result = $object->insert_discount($discountid); // This include link_to_invoice
1770 } else {
1771 setEventMessages($discount->error, $discount->errors, 'errors');
1772 $error++;
1773 break;
1774 }
1775 } else {
1776 // Positive line
1777 // we keep first type from product if exist, otherwise we keep type from line (free line) and at last default Product
1778 $product_type = $lines[$i]->product_type ?? ($lines[$i]->type ?? Product::TYPE_PRODUCT);
1779
1780 // Date start
1781 $date_start = false;
1782 if (isset($lines[$i]->date_debut_prevue)) {
1783 $date_start = $lines[$i]->date_debut_prevue;
1784 }
1785 if (isset($lines[$i]->date_debut_reel)) {
1786 $date_start = $lines[$i]->date_debut_reel;
1787 }
1788 if (isset($lines[$i]->date_start)) {
1789 $date_start = $lines[$i]->date_start;
1790 }
1791
1792 // Date end
1793 $date_end = false;
1794 if (isset($lines[$i]->date_fin_prevue)) {
1795 $date_end = $lines[$i]->date_fin_prevue;
1796 }
1797 if (isset($lines[$i]->date_fin_reel)) {
1798 $date_end = $lines[$i]->date_fin_reel;
1799 }
1800 if (isset($lines[$i]->date_end)) {
1801 $date_end = $lines[$i]->date_end;
1802 }
1803
1804 // Reset fk_parent_line for no child products and special product
1805 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
1806 $fk_parent_line = 0;
1807 }
1808
1809 // Extrafields
1810 if (method_exists($lines[$i], 'fetch_optionals')) {
1811 $lines[$i]->fetch_optionals();
1812 $array_options = $lines[$i]->array_options;
1813 }
1814
1815 $tva_tx = $lines[$i]->tva_tx;
1816 if (!empty($lines[$i]->vat_src_code) && !preg_match('/\‍(/', $tva_tx)) {
1817 $tva_tx .= ' ('.$lines[$i]->vat_src_code.')';
1818 }
1819
1820 // View third's localtaxes for NOW and do not use value from origin.
1821 // TODO Is this really what we want ? Yes if source is template invoice but what if proposal or order ?
1822 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty);
1823 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty);
1824
1825 $result = $object->addline(
1826 $desc,
1827 $lines[$i]->subprice,
1828 $lines[$i]->qty,
1829 $tva_tx,
1830 $localtax1_tx,
1831 $localtax2_tx,
1832 $lines[$i]->fk_product,
1833 $lines[$i]->remise_percent,
1834 $date_start,
1835 $date_end,
1836 0,
1837 $lines[$i]->info_bits,
1838 isset($lines[$i]->fk_remise_except) ? $lines[$i]->fk_remise_except : null,
1839 'HT',
1840 0,
1841 $product_type,
1842 $lines[$i]->rang,
1843 $lines[$i]->special_code,
1844 $object->origin,
1845 $lines[$i]->rowid,
1846 $fk_parent_line,
1847 isset($lines[$i]->fk_fournprice) ? $lines[$i]->fk_fournprice : null,
1848 $lines[$i]->pa_ht,
1849 $label,
1850 $array_options,
1851 $lines[$i]->situation_percent ?? 100,
1852 $lines[$i]->fk_prev_id ?? 0,
1853 $lines[$i]->fk_unit,
1854 0,
1855 '',
1856 1
1857 );
1858
1859 if ($result > 0) {
1860 $lineid = $result;
1861 } else {
1862 $lineid = 0;
1863 $error++;
1864 break;
1865 }
1866
1867 // Defined the new fk_parent_line
1868 if ($result > 0 && $lines[$i]->product_type == 9) {
1869 $fk_parent_line = $result;
1870 }
1871 }
1872 }
1873 } else {
1874 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
1875 $error++;
1876 }
1877 }
1878
1879 $object->update_price(1, 'auto', 0, $mysoc);
1880
1881 // Now we create same links to contact than the ones found on origin object
1882 /* Useless, already into the create
1883 if (getDolGlobalString('MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN')) {
1884 $originforcontact = $object->origin;
1885 $originidforcontact = $object->origin_id;
1886 if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
1887 {
1888 $originforcontact=$srcobject->origin;
1889 $originidforcontact=$srcobject->origin_id;
1890 }
1891 $sqlcontact = "SELECT code, fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
1892 $sqlcontact.= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$db->escape($originforcontact)."'";
1893
1894 $resqlcontact = $db->query($sqlcontact);
1895 if ($resqlcontact)
1896 {
1897 while($objcontact = $db->fetch_object($resqlcontact))
1898 {
1899 //print $objcontact->code.'-'.$objcontact->fk_socpeople."\n";
1900 $object->add_contact($objcontact->fk_socpeople, $objcontact->code);
1901 }
1902 }
1903 else dol_print_error($resqlcontact);
1904 }*/
1905
1906 // Hooks
1907 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id, 'objFrom' => $srcobject);
1908 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
1909 // modified by hook
1910 if ($reshook < 0) {
1911 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1912 $error++;
1913 }
1914 } else {
1915 setEventMessages($object->error, $object->errors, 'errors');
1916 $error++;
1917 }
1918 } else { // If some invoice's lines coming from page
1919 $id = $object->create($user);
1920
1921 for ($i = 1; $i <= $NBLINES; $i++) {
1922 if (GETPOSTINT('idprod'.$i)) {
1923 $product = new Product($db);
1924 $product->fetch(GETPOSTINT('idprod'.$i));
1925 $startday = dol_mktime(12, 0, 0, GETPOST('date_start'.$i.'month'), GETPOST('date_start'.$i.'day'), GETPOST('date_start'.$i.'year'));
1926 $endday = dol_mktime(12, 0, 0, GETPOST('date_end'.$i.'month'), GETPOST('date_end'.$i.'day'), GETPOST('date_end'.$i.'year'));
1927 $result = $object->addline($product->description, $product->price, price2num(GETPOST('qty'.$i), 'MS'), $product->tva_tx, $product->localtax1_tx, $product->localtax2_tx, GETPOSTINT('idprod'.$i), 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);
1928 }
1929 }
1930
1931 $object->update_price(1, 'auto', 0, $mysoc);
1932 }
1933 }
1934 }
1935
1936 // Situation invoices
1937 if (GETPOST('type') == Facture::TYPE_SITUATION && GETPOST('situations')) {
1938 if (empty($dateinvoice)) {
1939 $error++;
1940 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date"));
1941 setEventMessages($mesg, null, 'errors');
1942 } elseif ($dateinvoice > (dol_get_last_hour(dol_now('tzuserrel')) + getDolGlobalInt('INVOICE_MAX_FUTURE_DELAY'))) {
1943 $error++;
1944 setEventMessages($langs->trans("ErrorDateIsInFuture"), null, 'errors');
1945 $action = 'create';
1946 }
1947
1948 if (!(GETPOSTINT('situations') > 0)) {
1949 $error++;
1950 $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InvoiceSituation"));
1951 setEventMessages($mesg, null, 'errors');
1952 $action = 'create';
1953 }
1954
1955 if (!$error) {
1956 $result = $object->fetch(GETPOSTINT('situations'));
1957 $object->fk_facture_source = GETPOSTINT('situations');
1959
1960 if (!empty($origin) && !empty($originid)) {
1961 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1962
1963 $object->origin = $origin; // deprecated
1964 $object->origin_type = $origin;
1965 $object->origin_id = $originid;
1966
1967 // retained warranty
1968 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
1969 $retained_warranty = GETPOSTINT('retained_warranty');
1970 if (price2num($retained_warranty) > 0) {
1971 $object->retained_warranty = (float) price2num($retained_warranty);
1972 }
1973
1974 if (GETPOSTINT('retained_warranty_fk_cond_reglement') > 0) {
1975 $object->retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
1976 }
1977
1978 $retained_warranty_date_limit = GETPOST('retained_warranty_date_limit');
1979 if (!empty($retained_warranty_date_limit) && $db->jdate($retained_warranty_date_limit)) {
1980 $object->retained_warranty_date_limit = $db->jdate($retained_warranty_date_limit);
1981 }
1982 $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);
1983 }
1984
1985 foreach ($object->lines as $i => &$line) {
1986 $line->fk_prev_id = $line->id;
1987 $line->fetch_optionals();
1988 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
1989 $line->situation_percent = $line->get_allprev_progress($object->id);; // get good progress including credit note
1990 } else {
1991 $line->situation_percent = $line->get_prev_progress($object->id); // get good progress including credit note
1992 }
1993
1994 // The $line->situation_percent has been modified, so we must recalculate all amounts
1995 $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);
1996 $line->total_ht = $tabprice[0];
1997 $line->total_tva = $tabprice[1];
1998 $line->total_ttc = $tabprice[2];
1999 $line->total_localtax1 = $tabprice[9];
2000 $line->total_localtax2 = $tabprice[10];
2001 $line->multicurrency_total_ht = $tabprice[16];
2002 $line->multicurrency_total_tva = $tabprice[17];
2003 $line->multicurrency_total_ttc = $tabprice[18];
2004
2005 // If fk_remise_except defined we check if the reduction has already been applied
2006 if ($line->fk_remise_except) {
2007 $discount = new DiscountAbsolute($line->db);
2008 $result = $discount->fetch($line->fk_remise_except);
2009 if ($result > 0) {
2010 // Check if discount not already affected to another invoice
2011 if ($discount->fk_facture_line > 0) {
2012 $line->fk_remise_except = 0;
2013 }
2014 }
2015 }
2016 }
2017 }
2018
2019 $object->fetch_thirdparty();
2020 $object->date = $dateinvoice;
2021 $object->date_pointoftax = $date_pointoftax;
2022 $object->note_public = trim(GETPOST('note_public', 'restricthtml'));
2023 $object->note = trim(GETPOST('note', 'restricthtml'));
2024 $object->note_private = trim(GETPOST('note', 'restricthtml'));
2025 $object->ref_client = GETPOST('ref_client', 'alpha');
2026 $object->ref_customer = GETPOST('ref_client', 'alpha');
2027 $object->model_pdf = GETPOST('model', 'alpha');
2028 $object->fk_project = GETPOSTINT('projectid');
2029 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
2030 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
2031 //$object->remise_absolue =price2num(GETPOST('remise_absolue'), 'MU', 2);
2032 //$object->remise_percent = price2num(GETPOST('remise_percent'), '', 2);
2033 $object->fk_account = GETPOSTINT('fk_account');
2034
2035
2036 // Special properties of replacement invoice
2037
2038 $object->situation_counter += 1;
2039
2040 $id = $object->createFromCurrent($user);
2041 if ($id <= 0) {
2042 $mesg = $object->error;
2043 } else {
2044 $nextSituationInvoice = new Facture($db);
2045 $nextSituationInvoice->fetch($id);
2046
2047 // create extrafields with data from create form
2048 $extrafields->fetch_name_optionals_label($nextSituationInvoice->table_element);
2049 $ret = $extrafields->setOptionalsFromPost(null, $nextSituationInvoice);
2050 if ($ret > 0) {
2051 $nextSituationInvoice->insertExtraFields();
2052 }
2053
2054 // Hooks
2055 $parameters = array('origin_type' => $object->origin_type, 'origin_id' => $object->origin_id);
2056 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $nextSituationInvoice, $action); // Note that $action and $object may have been
2057 // modified by hook
2058 if ($reshook < 0) {
2059 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2060 $error++;
2061 }
2062 }
2063 }
2064 }
2065
2066 // End of object creation, we show it
2067 if ($id > 0 && !$error) {
2068 $db->commit();
2069
2070 // Define output language
2071 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && count($object->lines)) {
2072 $outputlangs = $langs;
2073 $newlang = '';
2074 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
2075 $newlang = GETPOST('lang_id', 'aZ09');
2076 }
2077 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2078 $newlang = $object->thirdparty->default_lang;
2079 }
2080 if (!empty($newlang)) {
2081 $outputlangs = new Translate("", $conf);
2082 $outputlangs->setDefaultLang($newlang);
2083 $outputlangs->load('products');
2084 }
2085 $model = $object->model_pdf;
2086 $ret = $object->fetch($id); // Reload to get new records
2087
2088 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2089 if ($result < 0) {
2090 setEventMessages($object->error, $object->errors, 'errors');
2091 }
2092 }
2093
2094 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id);
2095 exit();
2096 } else {
2097 $db->rollback();
2098 $action = 'create';
2099 $_GET["origin"] = $_POST["origin"]; // Keep GET and POST here ?
2100 $_GET["originid"] = $_POST["originid"]; // Keep GET and POST here ?
2101 setEventMessages($object->error, $object->errors, 'errors');
2102 }
2103 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
2104 // Define date start and date end for all line
2105 $alldate_start = dol_mktime(GETPOST('alldate_starthour'), GETPOST('alldate_startmin'), 0, GETPOST('alldate_startmonth'), GETPOST('alldate_startday'), GETPOST('alldate_startyear'));
2106 $alldate_end = dol_mktime(GETPOST('alldate_endhour'), GETPOST('alldate_endmin'), 0, GETPOST('alldate_endmonth'), GETPOST('alldate_endday'), GETPOST('alldate_endyear'));
2107 foreach ($object->lines as $line) {
2108 if ($line->product_type == 1) { // only service line
2109 $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);
2110 }
2111 }
2112 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
2113 // Define vat_rate
2114 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
2115 $vat_rate = str_replace('*', '', $vat_rate);
2116 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
2117 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
2118 foreach ($object->lines as $line) {
2119 $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);
2120 }
2121 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
2122 // Define vat_rate
2123 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
2124 $remise_percent = str_replace('*', '', $remise_percent);
2125 foreach ($object->lines as $line) {
2126 $tvatx= $line->tva_tx;
2127 if (!empty($line->vat_src_code)) {
2128 $tvatx .= ' ('.$line->vat_src_code.')';
2129 }
2130 $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, $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);
2131 }
2132 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && $usercancreate) { // Add a new line
2133 $langs->load('errors');
2134 $error = 0;
2135
2136 // Set if we used free entry or predefined product
2137 $predef = '';
2138 $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
2139
2140 $price_ht = '';
2141 $price_ht_devise = '';
2142 $price_ttc = '';
2143 $price_ttc_devise = '';
2144 $price_min = '';
2145 $price_min_ttc = '';
2146
2147 if (GETPOST('price_ht') !== '') {
2148 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
2149 }
2150 if (GETPOST('multicurrency_price_ht') !== '') {
2151 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
2152 }
2153 if (GETPOST('price_ttc') !== '') {
2154 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
2155 }
2156 if (GETPOST('multicurrency_price_ttc') !== '') {
2157 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
2158 }
2159
2160 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
2161 if ($prod_entry_mode == 'free') {
2162 $idprod = 0;
2163 } else {
2164 $idprod = GETPOSTINT('idprod');
2165
2166 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
2167 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
2168 $error++;
2169 }
2170 }
2171
2172 $tva_tx = GETPOST('tva_tx', 'alpha');
2173
2174 $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
2175 $remise_percent = (GETPOSTISSET('remise_percent'.$predef) ? price2num(GETPOST('remise_percent'.$predef, 'alpha'), '', 2) : 0);
2176 if (empty($remise_percent)) {
2177 $remise_percent = 0;
2178 }
2179
2180 // Extrafields
2181 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
2182 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
2183 // Unset extrafield
2184 if (is_array($extralabelsline)) {
2185 // Get extra fields
2186 foreach ($extralabelsline as $key => $value) {
2187 unset($_POST["options_".$key.$predef]);
2188 }
2189 }
2190
2191 if ((empty($idprod) || $idprod < 0) && ($price_ht < 0) && ($qty < 0)) {
2192 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2193 $error++;
2194 }
2195 if (!$prod_entry_mode) {
2196 if (GETPOST('type') < 0 && !GETPOST('search_idprod')) {
2197 setEventMessages($langs->trans('ErrorChooseBetweenFreeEntryOrPredefinedProduct'), null, 'errors');
2198 $error++;
2199 }
2200 }
2201 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
2202 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
2203 $error++;
2204 }
2205 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && (($price_ht < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht == '') && (($price_ht_devise < 0 && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) || $price_ht_devise == '') && $price_ttc === '' && $price_ttc_devise === '' && $object->type != Facture::TYPE_CREDIT_NOTE) { // Unit price can be 0 but not ''
2206 if (($price_ht < 0 || $price_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
2207 $langs->load("errors");
2208 if ($object->type == $object::TYPE_DEPOSIT) {
2209 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
2210 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
2211 } else {
2212 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
2213 }
2214 $error++;
2215 } else {
2216 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
2217 $error++;
2218 }
2219 }
2220 if ($qty == '') {
2221 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
2222 $error++;
2223 }
2224 if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) {
2225 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
2226 $error++;
2227 }
2228 if ($qty < 0) {
2229 $langs->load("errors");
2230 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
2231 $error++;
2232 }
2233
2234 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
2235 if ($combinations = GETPOST('combinations', 'array')) {
2236 //Check if there is a product with the given combination
2237 $prodcomb = new ProductCombination($db);
2238
2239 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
2240 $idprod = $res->fk_product_child;
2241 } else {
2242 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
2243 $error++;
2244 }
2245 }
2246 }
2247
2248 if (!$error && ($qty >= 0) && (!empty($product_desc) || (!empty($idprod) && $idprod > 0))) {
2249 $ret = $object->fetch($id);
2250 if ($ret < 0) {
2251 dol_print_error($db, $object->error);
2252 exit();
2253 }
2254 $ret = $object->fetch_thirdparty();
2255
2256 // Clean parameters
2257 $date_start = dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year'));
2258 $date_end = dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end'.$predef.'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year'));
2259 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
2260 $tva_npr = "";
2261
2262 // Define special_code for special lines
2263 $special_code = 0;
2264 // if (!GETPOST(qty)) $special_code=3; // Options should not exists on invoices
2265
2266 // Replaces $pu with that of the product
2267 // Replaces $desc with that of the product
2268 // Replaces $base_price_type with that of the product
2269 // Replaces $fk_unit with that of the product
2270 if (!empty($idprod) && $idprod > 0) {
2271 $prod = new Product($db);
2272 $prod->fetch($idprod);
2273
2274 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
2275
2276 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
2277 $pqp = (GETPOSTINT('pbq') ? GETPOSTINT('pbq') : 0);
2278
2279 $datapriceofproduct = $prod->getSellPrice($mysoc, $object->thirdparty, $pqp);
2280
2281 $pu_ht = $datapriceofproduct['pu_ht'];
2282 $pu_ttc = $datapriceofproduct['pu_ttc'];
2283 $price_min = $datapriceofproduct['price_min'];
2284 $price_min_ttc = (isset($datapriceofproduct['price_min_ttc'])) ? $datapriceofproduct['price_min_ttc'] : null;
2285 $price_base_type = empty($datapriceofproduct['price_base_type']) ? 'HT' : $datapriceofproduct['price_base_type'];
2286
2287 //$tva_tx = $datapriceofproduct['tva_tx'];
2288 //$tva_npr = $datapriceofproduct['tva_npr'];
2289 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
2290 $tmpprodvat = price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
2291
2292 // Set unit price to use
2293 // TODO We should not have this
2294 if (!empty($price_ht) || $price_ht === '0') {
2295 $pu_ht = price2num($price_ht, 'MU');
2296 $pu_ttc = price2num((float) $pu_ht * (1 + ($tmpvat / 100)), 'MU');
2297 } elseif (!empty($price_ht_devise) || $price_ht_devise === '0') {
2298 $pu_ht_devise = price2num($price_ht_devise, 'MU');
2299 $pu_ht = '';
2300 $pu_ttc = '';
2301 } elseif (!empty($price_ttc) || $price_ttc === '0') {
2302 $pu_ttc = price2num($price_ttc, 'MU');
2303 $pu_ht = price2num((float) $pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2304 } elseif ($tmpvat != $tmpprodvat) {
2305 // Is this still used ?
2306 if ($price_base_type != 'HT') {
2307 $pu_ht = price2num($pu_ttc / (1 + ($tmpvat / 100)), 'MU');
2308 } else {
2309 $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU');
2310 }
2311 }
2312
2313 $desc = '';
2314
2315 // Define output language
2316 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2317 $outputlangs = $langs;
2318 $newlang = '';
2319 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
2320 $newlang = GETPOST('lang_id', 'aZ09');
2321 }
2322 if (empty($newlang)) {
2323 $newlang = $object->thirdparty->default_lang;
2324 }
2325 if (!empty($newlang)) {
2326 $outputlangs = new Translate("", $conf);
2327 $outputlangs->setDefaultLang($newlang);
2328 $outputlangs->load('products');
2329 }
2330
2331 $desc = (!empty($prod->multilangs [$outputlangs->defaultlang] ["description"])) ? $prod->multilangs [$outputlangs->defaultlang] ["description"] : $prod->description;
2332 } else {
2333 $desc = $prod->description;
2334 }
2335
2336 //If text set in desc is the same as product descpription (as now it's preloaded) we add it only one time
2337 if ($product_desc == $desc && getDolGlobalString('PRODUIT_AUTOFILL_DESC')) {
2338 $product_desc = '';
2339 }
2340
2341 if (!empty($product_desc) && getDolGlobalString('MAIN_NO_CONCAT_DESCRIPTION')) {
2342 $desc = $product_desc;
2343 } else {
2344 $desc = dol_concatdesc($desc, $product_desc, '', getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION'));
2345 }
2346
2347 // Add custom code and origin country into description
2348 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
2349 $tmptxt = '(';
2350 // Define output language
2351 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2352 $outputlangs = $langs;
2353 $newlang = '';
2354 if (empty($newlang) && GETPOST('lang_id', 'alpha')) {
2355 $newlang = GETPOST('lang_id', 'alpha');
2356 }
2357 if (empty($newlang)) {
2358 $newlang = $object->thirdparty->default_lang;
2359 }
2360 if (!empty($newlang)) {
2361 $outputlangs = new Translate("", $conf);
2362 $outputlangs->setDefaultLang($newlang);
2363 $outputlangs->load('products');
2364 }
2365 if (!empty($prod->customcode)) {
2366 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomCode").': '.$prod->customcode;
2367 }
2368 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2369 $tmptxt .= ' - ';
2370 }
2371 if (!empty($prod->country_code)) {
2372 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, 0, $db, $outputlangs, 0);
2373 }
2374 } else {
2375 if (!empty($prod->customcode)) {
2376 $tmptxt .= $langs->transnoentitiesnoconv("CustomCode").': '.$prod->customcode;
2377 }
2378 if (!empty($prod->customcode) && !empty($prod->country_code)) {
2379 $tmptxt .= ' - ';
2380 }
2381 if (!empty($prod->country_code)) {
2382 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, 0, $db, $langs, 0);
2383 }
2384 }
2385 $tmptxt .= ')';
2386 $desc = dol_concatdesc($desc, $tmptxt);
2387 }
2388
2389 $type = $prod->type;
2390 $fk_unit = $prod->fk_unit;
2391 } else {
2392 if (!empty($price_ht)) {
2393 $pu_ht = price2num($price_ht, 'MU');
2394 } else {
2395 $pu_ht = '';
2396 }
2397 if (!empty($price_ttc)) {
2398 $pu_ttc = price2num($price_ttc, 'MU');
2399 } else {
2400 $pu_ttc = '';
2401 }
2402 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
2403 $tva_tx = str_replace('*', '', $tva_tx);
2404 if (empty($tva_tx)) {
2405 $tva_npr = 0;
2406 }
2407 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2408 $desc = $product_desc;
2409 $type = GETPOST('type');
2410 $fk_unit = GETPOST('units', 'alpha');
2411
2412 if ($pu_ttc && !$pu_ht) {
2413 $price_base_type = 'TTC';
2414 }
2415 }
2416
2417 // Define info_bits
2418 $info_bits = 0;
2419 if ($tva_npr) {
2420 $info_bits |= 0x01;
2421 }
2422
2423 // Local Taxes
2424 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
2425 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
2426
2427 $pu_ht_devise = price2num($price_ht_devise, '', 2);
2428 $pu_ttc_devise = price2num($price_ttc_devise, '', 2);
2429
2430 // Prepare a price equivalent for minimum price check
2431 $pu_equivalent = $pu_ht;
2432 $pu_equivalent_ttc = $pu_ttc;
2433
2434 $currency_tx = $object->multicurrency_tx;
2435
2436 // Check if we have a foreign currency
2437 // If so, we update the pu_equiv as the equivalent price in base currency
2438 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '') {
2439 $pu_equivalent = (float) $pu_ht_devise * $currency_tx;
2440 }
2441 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '') {
2442 $pu_equivalent_ttc = (float) $pu_ttc_devise * $currency_tx;
2443 }
2444
2445 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
2446 /*
2447 if ($pu_equivalent) {
2448 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
2449 $pu_equivalent_ttc = ...
2450 } else {
2451 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
2452 $pu_equivalent_ht = ...
2453 }
2454 */
2455
2456 // Margin
2457 $fournprice = price2num(GETPOST('fournprice'.$predef) ? GETPOST('fournprice'.$predef) : '');
2458 $buyingprice = price2num(GETPOST('buying_price'.$predef) != '' ? GETPOST('buying_price'.$predef) : ''); // If buying_price is '0', we must keep this value
2459
2460
2461 $price2num_pu_ht = price2num($pu_ht);
2462 $price2num_remise_percent = price2num($remise_percent);
2463 $price2num_price_min = price2num($price_min);
2464 $price2num_price_min_ttc = price2num($price_min_ttc);
2465 if (empty($price2num_pu_ht)) {
2466 $price2num_pu_ht = 0;
2467 }
2468 if (empty($price2num_remise_percent)) {
2469 $price2num_remise_percent = 0;
2470 }
2471 if (empty($price2num_price_min)) {
2472 $price2num_price_min = 0;
2473 }
2474 if (empty($price2num_price_min_ttc)) {
2475 $price2num_price_min_ttc = 0;
2476 }
2477
2478 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
2479 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
2480 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2481 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2482 setEventMessages($mesg, null, 'errors');
2483 $error++;
2484 } 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') {
2485 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2486 setEventMessages($mesg, null, 'errors');
2487 $error++;
2488 }
2489 }
2490
2491 if (!$error) {
2492 '@phan-var-force array<string,mixed> $lines';
2493 // Add batchinfo if the detail_batch array is defined
2494 if (isModEnabled('productbatch') && !empty($lines[$i]->detail_batch) && is_array($lines[$i]->detail_batch) && getDolGlobalString('INVOICE_INCUDE_DETAILS_OF_LOTS_SERIALS')) {
2495 $langs->load('productbatch');
2496 foreach ($lines[$i]->detail_batch as $batchline) {
2497 $desc .= ' '.$langs->trans('Batch').' '.$batchline->batch.' '.$langs->trans('printQty', $batchline->qty).' ';
2498 }
2499 }
2500
2501 // Insert line
2502 $result = $object->addline($desc, $pu_ht, $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, GETPOST('fk_parent_line'), $fournprice, $buyingprice, $label, $array_options, GETPOST('progress'), 0, $fk_unit, $pu_ht_devise);
2503
2504 if ($result > 0) {
2505 // Define output language and generate document
2506 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2507 $outputlangs = $langs;
2508 $newlang = '';
2509 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
2510 $newlang = GETPOST('lang_id', 'aZ09');
2511 }
2512 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2513 $newlang = $object->thirdparty->default_lang;
2514 }
2515 if (!empty($newlang)) {
2516 $outputlangs = new Translate("", $conf);
2517 $outputlangs->setDefaultLang($newlang);
2518 $outputlangs->load('products');
2519 }
2520 $model = $object->model_pdf;
2521 $ret = $object->fetch($id); // Reload to get new records
2522
2523 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
2524 if ($result < 0) {
2525 setEventMessages($object->error, $object->errors, 'errors');
2526 }
2527 }
2528
2529 unset($_POST['prod_entry_mode']);
2530 unset($_POST['qty']);
2531 unset($_POST['type']);
2532 unset($_POST['remise_percent']);
2533 unset($_POST['price_ht']);
2534 unset($_POST['multicurrency_price_ht']);
2535 unset($_POST['price_ttc']);
2536 unset($_POST['tva_tx']);
2537 unset($_POST['product_ref']);
2538 unset($_POST['product_label']);
2539 unset($_POST['product_desc']);
2540 unset($_POST['fournprice']);
2541 unset($_POST['buying_price']);
2542 unset($_POST['np_marginRate']);
2543 unset($_POST['np_markRate']);
2544 unset($_POST['dp_desc']);
2545 unset($_POST['idprod']);
2546 unset($_POST['units']);
2547 unset($_POST['date_starthour']);
2548 unset($_POST['date_startmin']);
2549 unset($_POST['date_startsec']);
2550 unset($_POST['date_startday']);
2551 unset($_POST['date_startmonth']);
2552 unset($_POST['date_startyear']);
2553 unset($_POST['date_endhour']);
2554 unset($_POST['date_endmin']);
2555 unset($_POST['date_endsec']);
2556 unset($_POST['date_endday']);
2557 unset($_POST['date_endmonth']);
2558 unset($_POST['date_endyear']);
2559 unset($_POST['situations']);
2560 unset($_POST['progress']);
2561 } else {
2562 setEventMessages($object->error, $object->errors, 'errors');
2563 }
2564
2565 $action = '';
2566 }
2567 }
2568 } elseif ($action == 'updateline' && $usercancreate && !GETPOST('cancel', 'alpha')) {
2569 if (!$object->fetch($id) > 0) {
2570 dol_print_error($db);
2571 }
2572 $object->fetch_thirdparty();
2573
2574 // Clean parameters
2575 $date_start = '';
2576 $date_end = '';
2577 $date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), GETPOST('date_startsec'), GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
2578 $date_end = dol_mktime(GETPOST('date_endhour'), GETPOST('date_endmin'), GETPOST('date_endsec'), GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear'));
2579 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml') ? GETPOST('product_desc', 'restricthtml') : GETPOST('desc', 'restricthtml'));
2580 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
2581 $vat_rate = str_replace('*', '', $vat_rate);
2582
2583 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
2584 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
2585
2586 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
2587 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
2588
2589 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
2590
2591 // Define info_bits
2592 $info_bits = 0;
2593 if (preg_match('/\*/', $vat_rate)) {
2594 $info_bits |= 0x01;
2595 }
2596
2597 // Define vat_rate
2598 $vat_rate = str_replace('*', '', $vat_rate);
2599 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty);
2600 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty);
2601
2602 // Add buying price
2603 $fournprice = price2num(GETPOST('fournprice') ? GETPOST('fournprice') : '');
2604 $buyingprice = price2num(GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''); // If buying_price is '0', we must keep this value
2605
2606 // Prepare a price equivalent for minimum price check
2607 $pu_equivalent = $pu_ht;
2608 $pu_equivalent_ttc = $pu_ttc;
2609
2610 $currency_tx = $object->multicurrency_tx;
2611
2612 // Check if we have a foreign currency
2613 // If so, we update the pu_equiv as the equivalent price in base currency
2614 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '') {
2615 $pu_equivalent = (float) $pu_ht_devise * (float) $currency_tx;
2616 }
2617 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '') {
2618 $pu_equivalent_ttc = (float) $pu_ttc_devise * (float) $currency_tx;
2619 }
2620
2621 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
2622 /*
2623 if ($pu_equivalent) {
2624 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
2625 $pu_equivalent_ttc = ...
2626 } else {
2627 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
2628 $pu_equivalent_ht = ...
2629 }
2630 */
2631
2632 // Extrafields
2633 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
2634 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
2635 // Unset extrafield
2636 if (is_array($extralabelsline)) {
2637 // Get extra fields
2638 foreach ($extralabelsline as $key => $value) {
2639 unset($_POST["options_".$key]);
2640 }
2641 }
2642
2643 // Define special_code for special lines
2644 $special_code = GETPOSTINT('special_code');
2645 if ($special_code == 3) {
2646 $special_code = 0; // Options should not exists on invoices
2647 }
2648
2649 $line = new FactureLigne($db);
2650 $line->fetch(GETPOSTINT('lineid'));
2651 $percent = $line->get_prev_progress($object->id);
2652 $progress = price2num(GETPOST('progress', 'alpha'));
2653
2654 if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->situation_cycle_ref > 0) {
2655 // in case of situation credit note
2656 if ($progress >= 0) {
2657 $mesg = $langs->trans("CantBeNullOrPositive");
2658 setEventMessages($mesg, null, 'warnings');
2659 $error++;
2660 $result = -1;
2661 } elseif ($progress < $line->situation_percent) { // TODO : use a modified $line->get_prev_progress($object->id) result
2662 $mesg = $langs->trans("CantBeLessThanMinPercent");
2663 setEventMessages($mesg, null, 'warnings');
2664 $error++;
2665 $result = -1;
2666 } elseif ($progress < $percent) {
2667 $mesg = '<div class="warning">'.$langs->trans("CantBeLessThanMinPercent").'</div>';
2668 setEventMessages($mesg, null, 'warnings');
2669 $error++;
2670 $result = -1;
2671 }
2672 }
2673
2674 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
2675
2676 // Check minimum price
2677 $productid = GETPOSTINT('productid');
2678 if (!empty($productid)) {
2679 $product = new Product($db);
2680 $product->fetch($productid);
2681
2682 $type = $product->type;
2683
2684 $price_min = $product->price_min;
2685 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2686 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
2687 }
2688 $price_min_ttc = $product->price_min_ttc;
2689 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2690 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
2691 }
2692
2693 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
2694
2695 // Check price is not lower than minimum (check is done only for standard or replacement invoices)
2696 if ($usermustrespectpricemin && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT)) {
2697 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2698 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2699 setEventMessages($mesg, null, 'errors');
2700 $error++;
2701 $action = 'editline';
2702 } 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') {
2703 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2704 setEventMessages($mesg, null, 'errors');
2705 $error++;
2706 $action = 'editline';
2707 }
2708 }
2709 } else {
2710 $type = GETPOST('type');
2711 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2712
2713 // Check parameters
2714 if (GETPOST('type') < 0) {
2715 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
2716 $error++;
2717 }
2718 }
2719 if ($qty < 0) {
2720 $langs->load("errors");
2721 setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
2722 $error++;
2723 }
2724 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 ''
2725 if (($pu_ht < 0 || $pu_ttc < 0) && !getDolGlobalString('FACTURE_ENABLE_NEGATIVE_LINES')) {
2726 $langs->load("errors");
2727 if ($object->type == $object::TYPE_DEPOSIT) {
2728 // Using negative lines on deposit lead to headach and blocking problems when you want to consume them.
2729 setEventMessages($langs->trans("ErrorLinesCantBeNegativeOnDeposits"), null, 'errors');
2730 } else {
2731 setEventMessages($langs->trans("ErrorFieldCantBeNegativeOnInvoice", $langs->transnoentitiesnoconv("UnitPriceHT"), $langs->transnoentitiesnoconv("CustomerAbsoluteDiscountShort")), null, 'errors');
2732 }
2733 $error++;
2734 } else {
2735 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
2736 $error++;
2737 }
2738 }
2739
2740 // Invoice situation
2741 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
2742 $previousprogress = $line->get_allprev_progress($line->fk_facture);
2743 $fullprogress = price2num(GETPOST('progress', 'alpha'));
2744
2745 if ($fullprogress < $previousprogress) {
2746 $error++;
2747 setEventMessages($langs->trans('CantBeLessThanMinPercent'), null, 'errors');
2748 }
2749
2750 // Max 100%
2751 if ($fullprogress > 100) {
2752 $fullprogress = 100;
2753 }
2754 $addprogress = $fullprogress - $previousprogress;
2755 } else {
2756 $addprogress = price2num(GETPOST('progress', 'alpha'));
2757 }
2758
2759 // Update line
2760 if (!$error) {
2761 if (empty($usercancreatemargin)) {
2762 foreach ($object->lines as &$line) {
2763 if ($line->id == GETPOSTINT('lineid')) {
2764 $fournprice = $line->fk_fournprice;
2765 $buyingprice = $line->pa_ht;
2766 break;
2767 }
2768 }
2769 }
2770
2771 $price_base_type = 'HT';
2772 $pu = $pu_ht;
2773 if (empty($pu) && !empty($pu_ttc)) {
2774 $pu = $pu_ttc;
2775 $price_base_type = 'TTC';
2776 }
2777
2778 $result = $object->updateline(
2779 GETPOSTINT('lineid'),
2780 $description,
2781 $pu,
2782 $qty,
2783 $remise_percent,
2784 $date_start,
2785 $date_end,
2786 $vat_rate,
2787 $localtax1_rate,
2788 $localtax2_rate,
2789 $price_base_type,
2790 $info_bits,
2791 $type,
2792 GETPOSTINT('fk_parent_line'),
2793 0,
2794 $fournprice,
2795 $buyingprice,
2796 $label,
2797 $special_code,
2798 $array_options,
2799 $addprogress,
2800 GETPOST('units', 'alpha'),
2801 $pu_ht_devise
2802 );
2803
2804 if ($result >= 0) {
2805 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2806 // Define output language
2807 $outputlangs = $langs;
2808 $newlang = '';
2809 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
2810 $newlang = GETPOST('lang_id', 'aZ09');
2811 }
2812 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
2813 $newlang = $object->thirdparty->default_lang;
2814 }
2815 if (!empty($newlang)) {
2816 $outputlangs = new Translate("", $conf);
2817 $outputlangs->setDefaultLang($newlang);
2818 $outputlangs->load('products');
2819 }
2820
2821 $ret = $object->fetch($id); // Reload to get new records
2822 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2823 }
2824
2825 unset($_POST['qty']);
2826 unset($_POST['type']);
2827 unset($_POST['productid']);
2828 unset($_POST['remise_percent']);
2829 unset($_POST['price_ht']);
2830 unset($_POST['multicurrency_price_ht']);
2831 unset($_POST['price_ttc']);
2832 unset($_POST['tva_tx']);
2833 unset($_POST['product_ref']);
2834 unset($_POST['product_label']);
2835 unset($_POST['product_desc']);
2836 unset($_POST['fournprice']);
2837 unset($_POST['buying_price']);
2838 unset($_POST['np_marginRate']);
2839 unset($_POST['np_markRate']);
2840 unset($_POST['dp_desc']);
2841 unset($_POST['idprod']);
2842 unset($_POST['units']);
2843 unset($_POST['date_starthour']);
2844 unset($_POST['date_startmin']);
2845 unset($_POST['date_startsec']);
2846 unset($_POST['date_startday']);
2847 unset($_POST['date_startmonth']);
2848 unset($_POST['date_startyear']);
2849 unset($_POST['date_endhour']);
2850 unset($_POST['date_endmin']);
2851 unset($_POST['date_endsec']);
2852 unset($_POST['date_endday']);
2853 unset($_POST['date_endmonth']);
2854 unset($_POST['date_endyear']);
2855 unset($_POST['situations']);
2856 unset($_POST['progress']);
2857 } else {
2858 setEventMessages($object->error, $object->errors, 'errors');
2859 }
2860 }
2861 } elseif ($action == 'updatealllines' && $usercancreate && GETPOST('all_percent') == $langs->trans('Modifier')) { // Update all lines of situation invoice
2862 if (!$object->fetch($id) > 0) {
2863 dol_print_error($db);
2864 }
2865 if (GETPOST('all_progress') != "") {
2866 $all_progress = GETPOSTINT('all_progress');
2867 foreach ($object->lines as $line) {
2868 if (getDolGlobalInt('INVOICE_USE_SITUATION') == 2) {
2869 $percent = $line->get_allprev_progress($object->id);
2870 } else {
2871 $percent = $line->get_prev_progress($object->id);
2872 }
2873 if ((float) $all_progress < (float) $percent) {
2874 $mesg = $langs->trans("Line").' '.$i.' : '.$langs->trans("CantBeLessThanMinPercent");
2875 setEventMessages($mesg, null, 'warnings');
2876 $result = -1;
2877 } else {
2878 $object->update_percent($line, GETPOST('all_progress'), false);
2879 }
2880 }
2881 $object->update_price(1);
2882 }
2883 } elseif ($action == 'updateline' && $usercancreate && !$cancel) {
2884 header('Location: '.$_SERVER["PHP_SELF"].'?facid='.$id); // To show again edited page
2885 exit();
2886 } elseif ($action == 'confirm_situationout' && $confirm == 'yes' && $usercancreate) {
2887 // Outing situation invoice from cycle
2888 $object->fetch($id, '', '', 0, true);
2889
2890 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
2892 && $usercancreate
2893 && !$objectidnext
2894 && $object->is_last_in_cycle()
2895 && $usercanunvalidate
2896 ) {
2897 $outingError = 0;
2898 $newCycle = $object->newCycle(); // we need to keep the "situation behavior" so we place it on a new situation cycle
2899 if ($newCycle > 1) {
2900 // Search credit notes
2901 $lastCycle = $object->situation_cycle_ref;
2902 $lastSituationCounter = $object->situation_counter;
2903 $linkedCreditNotesList = array();
2904
2905 if (count($object->tab_next_situation_invoice) > 0) {
2906 foreach ($object->tab_next_situation_invoice as $next_invoice) {
2907 if ($next_invoice->type == Facture::TYPE_CREDIT_NOTE
2908 && $next_invoice->situation_counter == $object->situation_counter
2909 && $next_invoice->fk_facture_source == $object->id
2910 ) {
2911 $linkedCreditNotesList[] = $next_invoice->id;
2912 }
2913 }
2914 }
2915
2916 $object->situation_cycle_ref = $newCycle;
2917 $object->situation_counter = 1;
2918 $object->situation_final = 0;
2919 if ($object->update($user) > 0) {
2920 $errors = 0;
2921 if (count($linkedCreditNotesList) > 0) {
2922 // now, credit note must follow
2923 $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
2924 $sql .= ' SET situation_cycle_ref = '.((int) $newCycle);
2925 $sql .= ' , situation_final=0';
2926 $sql .= ' , situation_counter='.((int) $object->situation_counter);
2927 $sql .= ' WHERE rowid IN ('.$db->sanitize(implode(',', $linkedCreditNotesList)).')';
2928
2929 $resql = $db->query($sql);
2930 if (!$resql) {
2931 $errors++;
2932 }
2933
2934 // Change each progression percent on each lines
2935 foreach ($object->lines as $line) {
2936 // no traitement for special product
2937 if ($line->product_type == 9) {
2938 continue;
2939 }
2940
2941
2942 if (!empty($object->tab_previous_situation_invoice)) {
2943 // search the last invoice in cycle
2944 $lineIndex = count($object->tab_previous_situation_invoice) - 1;
2945 $searchPreviousInvoice = true;
2946 while ($searchPreviousInvoice) {
2947 if ($object->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_SITUATION || $lineIndex < 1) {
2948 $searchPreviousInvoice = false; // find, exit;
2949 break;
2950 } else {
2951 $lineIndex--; // go to previous invoice in cycle
2952 }
2953 }
2954
2955
2956 $maxPrevSituationPercent = 0;
2957 foreach ($object->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
2958 if ($prevLine->id == $line->fk_prev_id) {
2959 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
2960 }
2961 }
2962
2963
2964 $line->situation_percent -= $maxPrevSituationPercent;
2965
2966 if ($line->update() < 0) {
2967 $errors++;
2968 }
2969 }
2970 }
2971 }
2972
2973 if (!$errors) {
2974 setEventMessages($langs->trans('Updated'), null, 'mesgs');
2975 header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
2976 } else {
2977 setEventMessages($langs->trans('ErrorOutingSituationInvoiceCreditNote'), array(), 'errors');
2978 }
2979 } else {
2980 setEventMessages($langs->trans('ErrorOutingSituationInvoiceOnUpdate'), array(), 'errors');
2981 }
2982 } else {
2983 setEventMessages($langs->trans('ErrorFindNextSituationInvoice'), array(), 'errors');
2984 }
2985 }
2986 } elseif ($action == 'import_lines_from_object'
2987 && $usercancreate
2988 && $object->status == Facture::STATUS_DRAFT
2990 // add lines from objectlinked
2991 $fromElement = GETPOST('fromelement');
2992 $fromElementid = GETPOST('fromelementid');
2993 $importLines = GETPOST('line_checkbox');
2994
2995 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && ctype_alpha($fromElement) && !empty($fromElementid)) {
2996 if ($fromElement == 'commande') {
2997 dol_include_once('/'.$fromElement.'/class/'.$fromElement.'.class.php');
2998 $lineClassName = 'OrderLine';
2999 } elseif ($fromElement == 'propal') {
3000 dol_include_once('/comm/'.$fromElement.'/class/'.$fromElement.'.class.php');
3001 $lineClassName = 'PropaleLigne';
3002 }
3003 $nextRang = count($object->lines) + 1;
3004 $importCount = 0;
3005 $error = 0;
3006 foreach ($importLines as $lineId) {
3007 $lineId = intval($lineId);
3008 $originLine = new $lineClassName($db);
3009 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
3010 $originLine->fetch_optionals();
3011 $desc = $originLine->desc;
3012 $pu_ht = $originLine->subprice;
3013 $qty = $originLine->qty;
3014 $txtva = $originLine->tva_tx;
3015 $txlocaltax1 = $originLine->localtax1_tx;
3016 $txlocaltax2 = $originLine->localtax2_tx;
3017 $fk_product = $originLine->fk_product;
3018 $remise_percent = $originLine->remise_percent;
3019 $date_start = $originLine->date_start;
3020 $date_end = $originLine->date_end;
3021 $fk_code_ventilation = 0;
3022 $info_bits = $originLine->info_bits;
3023 $fk_remise_except = $originLine->fk_remise_except;
3024 $price_base_type = 'HT';
3025 $pu_ttc = 0;
3026 $type = $originLine->product_type;
3027 $rang = $nextRang++;
3028 $special_code = $originLine->special_code;
3029 $origin = $originLine->element;
3030 $origin_id = $originLine->id;
3031 $fk_parent_line = 0;
3032 $fk_fournprice = $originLine->fk_fournprice;
3033 $pa_ht = $originLine->pa_ht;
3034 $label = $originLine->label;
3035 $array_options = $originLine->array_options;
3036 if ($object->type == Facture::TYPE_SITUATION) {
3037 $situation_percent = 0;
3038 } else {
3039 $situation_percent = 100;
3040 }
3041 $fk_prev_id = 0;
3042 $fk_unit = $originLine->fk_unit;
3043 $pu_ht_devise = $originLine->multicurrency_subprice;
3044
3045 $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);
3046
3047 if ($res > 0) {
3048 $importCount++;
3049 } else {
3050 $error++;
3051 }
3052 } else {
3053 $error++;
3054 }
3055 }
3056
3057 if ($error) {
3058 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
3059 }
3060 }
3061 }
3062
3063 // Actions when printing a doc from card
3064 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
3065
3066 // Actions to send emails
3067 if (empty($id)) {
3068 $id = $facid;
3069 }
3070 $triggersendname = 'BILL_SENTBYMAIL';
3071 $paramname = 'id';
3072 $autocopy = 'MAIN_MAIL_AUTOCOPY_INVOICE_TO';
3073 $trackid = 'inv'.$object->id;
3074 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
3075
3076 // Actions to build doc
3077 $upload_dir = $conf->invoice->multidir_output[!empty($object->entity) ? $object->entity : $conf->entity];
3078 $permissiontoadd = $usercancreate;
3079 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
3080
3081
3082 if ($action == 'update_extras') {
3083 $object->oldcopy = dol_clone($object, 2);
3084 $attribute_name = GETPOST('attribute', 'restricthtml');
3085
3086 // Fill array 'array_options' with data from add form
3087 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
3088 if ($ret < 0) {
3089 $error++;
3090 }
3091
3092 if (!$error) {
3093 // Actions on extra fields
3094 $result = $object->updateExtraField($attribute_name, 'BILL_MODIFY');
3095 if ($result < 0) {
3096 setEventMessages($object->error, $object->errors, 'errors');
3097 $error++;
3098 }
3099 }
3100
3101 if ($error) {
3102 $action = 'edit_extras';
3103 }
3104 }
3105
3106 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB') && $usercancreate) {
3107 if ($action == 'addcontact') {
3108 $result = $object->fetch($id);
3109
3110 if ($result > 0 && $id > 0) {
3111 $contactid = (GETPOST('userid') ? GETPOST('userid') : GETPOST('contactid'));
3112 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
3113 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
3114 }
3115
3116 if ($result >= 0) {
3117 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3118 exit();
3119 } else {
3120 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
3121 $langs->load("errors");
3122 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
3123 } else {
3124 setEventMessages($object->error, $object->errors, 'errors');
3125 }
3126 }
3127 } elseif ($action == 'swapstatut') {
3128 // toggle the status of a contact
3129 if ($object->fetch($id)) {
3130 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
3131 } else {
3132 dol_print_error($db);
3133 }
3134 } elseif ($action == 'deletecontact') {
3135 // Delete a contact
3136 $object->fetch($id);
3137 $result = $object->delete_contact($lineid);
3138
3139 if ($result >= 0) {
3140 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
3141 exit();
3142 } else {
3143 dol_print_error($db);
3144 }
3145 }
3146
3147 if ($error) {
3148 $action = 'edit_extras';
3149 }
3150 }
3151}
3152
3153
3154/*
3155 * View
3156 */
3157
3158
3159$form = new Form($db);
3160$formother = new FormOther($db);
3161$formfile = new FormFile($db);
3162$formmargin = new FormMargin($db);
3163$soc = new Societe($db);
3164$paymentstatic = new Paiement($db);
3165$bankaccountstatic = new Account($db);
3166if (isModEnabled('project')) {
3167 $formproject = new FormProjets($db);
3168}
3169
3170$now = dol_now();
3171
3172$title = $object->ref." - ".$langs->trans('Card');
3173if ($action == 'create') {
3174 $title = $langs->trans("NewBill");
3175}
3176$help_url = "EN:Customers_Invoices|FR:Factures_Clients|ES:Facturas_a_clientes";
3177
3178llxHeader('', $title, $help_url);
3179
3180// Mode creation
3181
3182if ($action == 'create') {
3183 $facturestatic = new Facture($db);
3184 $extrafields->fetch_name_optionals_label($facturestatic->table_element);
3185
3186 print load_fiche_titre($langs->trans('NewBill'), '', 'bill');
3187
3188 if ($socid > 0) {
3189 $res = $soc->fetch($socid);
3190 }
3191
3192 $currency_code = $conf->currency;
3193
3194 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
3195 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
3196 $fk_account = GETPOSTINT('fk_account');
3197
3198 // Load objectsrc
3199 //$remise_absolue = 0;
3200 if (!empty($origin) && !empty($originid)) {
3201 // Parse element/subelement (ex: project_task)
3202 $element = $subelement = $origin;
3203 $regs = array();
3204 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
3205 $element = $regs[1];
3206 $subelement = $regs[2];
3207 }
3208
3209 if ($element == 'project') {
3210 $projectid = $originid;
3211
3212 if (empty($cond_reglement_id)) {
3213 $cond_reglement_id = $soc->cond_reglement_id;
3214 }
3215 if (empty($mode_reglement_id)) {
3216 $mode_reglement_id = $soc->mode_reglement_id;
3217 }
3218 if (empty($fk_account)) {
3219 $fk_account = $soc->fk_account;
3220 }
3221 if (!$remise_percent) {
3222 $remise_percent = $soc->remise_percent;
3223 }
3224 if (!$dateinvoice) {
3225 // Do not set 0 here (0 for a date is 1970)
3226 $dateinvoice = (empty($dateinvoice) ? (!getDolGlobalString('MAIN_AUTOFILL_DATE') ? -1 : '') : $dateinvoice);
3227 }
3228 } else {
3229 // For compatibility
3230 if ($element == 'order' || $element == 'commande') {
3231 $element = $subelement = 'commande';
3232 }
3233 if ($element == 'propal') {
3234 $element = 'comm/propal';
3235 $subelement = 'propal';
3236 }
3237 if ($element == 'contract') {
3238 $element = $subelement = 'contrat';
3239 }
3240 if ($element == 'shipping') {
3241 $element = $subelement = 'expedition';
3242 }
3243
3244 dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
3245
3246 $classname = ucfirst($subelement);
3247 $objectsrc = new $classname($db);
3248 $objectsrc->fetch($originid);
3249 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
3250 $objectsrc->fetch_lines();
3251 }
3252 $objectsrc->fetch_thirdparty();
3253
3254 $projectid = (!empty($projectid) ? $projectid : $objectsrc->fk_project);
3255 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : ''));
3256
3257 // only if socid not filled else it's already done above
3258 if (empty($socid)) {
3259 $soc = $objectsrc->thirdparty;
3260 }
3261
3262 $dateinvoice = (empty($dateinvoice) ? (!getDolGlobalString('MAIN_AUTOFILL_DATE') ? -1 : '') : $dateinvoice);
3263
3264 if ($element == 'expedition') {
3265 $ref_client = (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : '');
3266
3267 $elem = $subelem = $objectsrc->origin;
3268 $expeoriginid = $objectsrc->origin_id;
3269 dol_include_once('/'.$elem.'/class/'.$subelem.'.class.php');
3270 $classname = ucfirst($subelem);
3271
3272 $expesrc = new $classname($db);
3273 $expesrc->fetch($expeoriginid);
3274
3275 $cond_reglement_id = (!empty($expesrc->cond_reglement_id) ? $expesrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 1));
3276 $mode_reglement_id = (!empty($expesrc->mode_reglement_id) ? $expesrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
3277 $fk_account = (!empty($expesrc->fk_account) ? $expesrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : 0));
3278 //$remise_percent = (!empty($expesrc->remise_percent) ? $expesrc->remise_percent : (!empty($soc->remise_percent) ? $soc->remise_percent : 0));
3279 //$remise_absolue = (!empty($expesrc->remise_absolue) ? $expesrc->remise_absolue : (!empty($soc->remise_absolue) ? $soc->remise_absolue : 0));
3280
3281 if (isModEnabled('multicurrency')) {
3282 $currency_code = (!empty($expesrc->multicurrency_code) ? $expesrc->multicurrency_code : (!empty($soc->multicurrency_code) ? $soc->multicurrency_code : $objectsrc->multicurrency_code));
3283 $currency_tx = (!empty($expesrc->multicurrency_tx) ? $expesrc->multicurrency_tx : (!empty($soc->multicurrency_tx) ? $soc->multicurrency_tx : $objectsrc->multicurrency_tx));
3284 }
3285
3286 //Replicate extrafields
3287 $expesrc->fetch_optionals();
3288 $object->array_options = $expesrc->array_options;
3289 } else {
3290 $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 0));
3291 $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
3292 $fk_account = (!empty($objectsrc->fk_account) ? $objectsrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : 0));
3293 //$remise_percent = (!empty($objectsrc->remise_percent) ? $objectsrc->remise_percent : (!empty($soc->remise_percent) ? $soc->remise_percent : 0));
3294 //$remise_absolue = (!empty($objectsrc->remise_absolue) ? $objectsrc->remise_absolue : (!empty($soc->remise_absolue) ? $soc->remise_absolue : 0));
3295
3296 if (isModEnabled('multicurrency')) {
3297 if (!empty($objectsrc->multicurrency_code)) {
3298 $currency_code = $objectsrc->multicurrency_code;
3299 }
3300 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
3301 $currency_tx = $objectsrc->multicurrency_tx;
3302 }
3303 }
3304
3305 // Replicate extrafields
3306 $objectsrc->fetch_optionals();
3307 $object->array_options = $objectsrc->array_options;
3308 }
3309 }
3310 } else {
3311 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
3312 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
3313 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
3314 //$remise_percent = $soc->remise_percent;
3315 //$remise_absolue = 0;
3316 $dateinvoice = (empty($dateinvoice) ? (!getDolGlobalString('MAIN_AUTOFILL_DATE') ? -1 : '') : $dateinvoice); // Do not set 0 here (0 for a date is 1970)
3317
3318 if (isModEnabled('multicurrency') && !empty($soc->multicurrency_code)) {
3319 $currency_code = $soc->multicurrency_code;
3320 }
3321 }
3322
3323 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
3324 if (!GETPOST('changecompany')) {
3325 if (GETPOSTISSET('cond_reglement_id')) {
3326 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
3327 }
3328 if (GETPOSTISSET('mode_reglement_id')) {
3329 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
3330 }
3331 if (GETPOSTISSET('cond_reglement_id')) {
3332 $fk_account = GETPOSTINT('fk_account');
3333 }
3334 }
3335
3336 // when payment condition is empty (means not override by payment condition form a other object, like third-party), try to use default value
3337 if (empty($cond_reglement_id)) {
3338 $cond_reglement_id = GETPOSTINT("cond_reglement_id");
3339 }
3340
3341 // when payment mode is empty (means not override by payment mode form a other object, like third-party), try to use default value
3342 if (empty($mode_reglement_id)) {
3343 $mode_reglement_id = GETPOSTINT("mode_reglement_id");
3344 }
3345
3346 // when bank account is empty (means not override by payment mode form a other object, like third-party), try to use default value
3347 // if ($socid > 0 && $fk_account) { // A company has already been set and it has a default fk_account
3348 // $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
3349 // } else { // No company forced
3350 // $fk_account = GETPOST("fk_account", 'int');
3351 // }
3352
3353 if (!empty($soc->id)) {
3354 $absolute_discount = $soc->getAvailableDiscounts();
3355 }
3356 $note_public = $object->getDefaultCreateValueFor('note_public', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_public : null));
3357 $note_private = $object->getDefaultCreateValueFor('note_private', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && getDolGlobalString('FACTURE_REUSE_NOTES_ON_CREATE_FROM')) ? $objectsrc->note_private : null));
3358
3359 if (!empty($conf->use_javascript_ajax)) {
3360 require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3361 print ajax_combobox('fac_replacement');
3362 print ajax_combobox('fac_avoir');
3363 print ajax_combobox('situations');
3364 }
3365
3366 if ($origin == 'contrat') {
3367 $langs->load("admin");
3368 $text = $langs->trans("ToCreateARecurringInvoice");
3369 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGene", $langs->transnoentitiesnoconv("MenuFinancial"), $langs->transnoentitiesnoconv("BillsCustomers"), $langs->transnoentitiesnoconv("ListOfTemplates"));
3370 if (!getDolGlobalString('INVOICE_DISABLE_AUTOMATIC_RECURRING_INVOICE')) {
3371 $text .= ' '.$langs->trans("ToCreateARecurringInvoiceGeneAuto", $langs->transnoentitiesnoconv('Module2300Name'));
3372 }
3373 print info_admin($text, 0, 0, 0, 'opacitymedium').'<br>';
3374 }
3375
3376 print '<form name="add" action="'.$_SERVER["PHP_SELF"].'" method="POST" id="formtocreate" name="formtocreate">';
3377 print '<input type="hidden" name="token" value="'.newToken().'">';
3378 print '<input type="hidden" name="action" id="formtocreateaction" value="add">';
3379 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
3380 if ($soc->id > 0) {
3381 print '<input type="hidden" name="socid" value="'.$soc->id.'">'."\n";
3382 }
3383 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
3384 print '<input name="ref" type="hidden" value="provisoire">';
3385 print '<input name="ref_client" type="hidden" value="'.$ref_client.'">';
3386 print '<input name="force_cond_reglement_id" type="hidden" value="0">';
3387 print '<input name="force_mode_reglement_id" type="hidden" value="0">';
3388 print '<input name="force_fk_account" type="hidden" value="0">';
3389 print '<input type="hidden" name="origin" value="'.$origin.'">';
3390 print '<input type="hidden" name="originid" value="'.$originid.'">';
3391 print '<input type="hidden" name="originentity" value="'.GETPOSTINT('originentity').'">';
3392 if (!empty($currency_tx)) {
3393 print '<input type="hidden" name="originmulticurrency_tx" value="'.$currency_tx.'">';
3394 }
3395
3396 print dol_get_fiche_head();
3397
3398 // Call Hook tabContentCreateInvoice
3399 $parameters = array();
3400 // Note that $action and $object may be modified by hook
3401 $reshook = $hookmanager->executeHooks('tabContentCreateInvoice', $parameters, $object, $action);
3402 if (empty($reshook)) {
3403 print '<table class="border centpercent">';
3404
3405 $exampletemplateinvoice = new FactureRec($db);
3406 $invoice_predefined = new FactureRec($db);
3407 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
3408 $invoice_predefined->fetch(GETPOSTINT('fac_rec'));
3409 }
3410
3411 // Thirdparty
3412 if ($soc->id > 0 && (!GETPOSTINT('fac_rec') || !empty($invoice_predefined->frequency))) {
3413 // If thirdparty known and not a predefined invoiced without a recurring rule
3414 print '<tr><td class="fieldrequired">'.$langs->trans('Customer').'</td>';
3415 print '<td colspan="2">';
3416 print $soc->getNomUrl(1, 'customer');
3417 print '<input type="hidden" name="socid" value="'.$soc->id.'">';
3418 // Outstanding Bill
3419 $arrayoutstandingbills = $soc->getOutstandingBills();
3420 $outstandingBills = $arrayoutstandingbills['opened'];
3421 print ' - <span class="opacitymedium">'.$langs->trans('CurrentOutstandingBill').':</span> ';
3422 print '<span class="amount">'.price($outstandingBills, 0, $langs, 0, 0, -1, $conf->currency).'</span>';
3423 if ($soc->outstanding_limit != '') {
3424 if ($outstandingBills > $soc->outstanding_limit) {
3425 print img_warning($langs->trans("OutstandingBillReached"));
3426 }
3427 print ' / '.price($soc->outstanding_limit, 0, $langs, 0, 0, -1, $conf->currency);
3428 }
3429 print '</td>';
3430 print '</tr>'."\n";
3431 } else {
3432 print '<tr><td class="fieldrequired">'.$langs->trans('Customer').'</td>';
3433 print '<td colspan="2">';
3434 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
3435 print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company($soc->id, 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 widthcentpercentminusxx maxwidth500');
3436 // Option to reload page to retrieve customer information.
3437 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
3438 print '<script>
3439 $(document).ready(function() {
3440 $("#socid").change(function() {
3441 /*
3442 console.log("Submit page");
3443 $(\'input[name="action"]\').val(\'create\');
3444 $(\'input[name="force_cond_reglement_id"]\').val(\'1\');
3445 $(\'input[name="force_mode_reglement_id"]\').val(\'1\');
3446 $(\'input[name="force_fk_account"]\').val(\'1\');
3447 $("#formtocreate").submit(); */
3448
3449 // For company change, we must submit page with action=create instead of action=add
3450 console.log("We have changed the company - Resubmit page");
3451 jQuery("input[name=changecompany]").val("1");
3452 jQuery("#formtocreateaction").val("create");
3453 jQuery("#formtocreate").submit();
3454 });
3455 });
3456 </script>';
3457 }
3458 if (!GETPOSTINT('fac_rec')) {
3459 print ' <a href="'.DOL_URL_ROOT.'/societe/card.php?action=create&client=3&fournisseur=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create').'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
3460 }
3461 print '</td>';
3462 print '</tr>'."\n";
3463 }
3464
3465 // Overwrite some values if creation of invoice is from a predefined invoice
3466 if (empty($origin) && empty($originid) && GETPOSTINT('fac_rec') > 0) {
3467 $invoice_predefined->fetch(GETPOSTINT('fac_rec'));
3468
3469 $dateinvoice = $invoice_predefined->date_when; // To use next gen date by default later
3470 if (empty($projectid)) {
3471 $projectid = $invoice_predefined->fk_project;
3472 }
3473 $cond_reglement_id = $invoice_predefined->cond_reglement_id;
3474 $mode_reglement_id = $invoice_predefined->mode_reglement_id;
3475 $fk_account = $invoice_predefined->fk_account;
3476 $note_public = $invoice_predefined->note_public;
3477 $note_private = $invoice_predefined->note_private;
3478
3479 if (!empty($invoice_predefined->multicurrency_code)) {
3480 $currency_code = $invoice_predefined->multicurrency_code;
3481 }
3482 if (!empty($invoice_predefined->multicurrency_tx)) {
3483 $currency_tx = $invoice_predefined->multicurrency_tx;
3484 }
3485
3486 $sql = 'SELECT r.rowid, r.titre as title, r.total_ttc';
3487 $sql .= ' FROM '.MAIN_DB_PREFIX.'facture_rec as r';
3488 $sql .= ' WHERE r.fk_soc = '.((int) $invoice_predefined->socid);
3489
3490 $resql = $db->query($sql);
3491 if ($resql) {
3492 $num = $db->num_rows($resql);
3493 $i = 0;
3494
3495 if ($num > 0) {
3496 print '<tr><td>'.$langs->trans('CreateFromRepeatableInvoice').'</td><td>';
3497 //print '<input type="hidden" name="fac_rec" id="fac_rec" value="'.GETPOST('fac_rec', 'int').'">';
3498 print '<select class="flat" id="fac_rec" name="fac_rec">'; // We may want to change the template to use
3499 print '<option value="0" selected></option>';
3500 while ($i < $num) {
3501 $objp = $db->fetch_object($resql);
3502 print '<option value="'.$objp->rowid.'"';
3503 if (GETPOSTINT('fac_rec') == $objp->rowid) {
3504 print ' selected';
3505 $exampletemplateinvoice->fetch(GETPOSTINT('fac_rec'));
3506 }
3507 print '>'.$objp->title.' ('.price($objp->total_ttc).' '.$langs->trans("TTC").')</option>';
3508 $i++;
3509 }
3510 print '</select>';
3511
3512 print ajax_combobox("fac_rec");
3513
3514 // Option to reload page to retrieve customer information. Note, this clear other input
3515 if (!getDolGlobalString('RELOAD_PAGE_ON_TEMPLATE_CHANGE_DISABLED')) {
3516 print '<script type="text/javascript">
3517 $(document).ready(function() {
3518 $("#fac_rec").change(function() {
3519 console.log("We have changed the template invoice - Reload page");
3520 var fac_rec = $(this).val();
3521 var socid = $(\'#socid\').val();
3522 // 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.
3523 window.location.href = "'.$_SERVER["PHP_SELF"].'?action=create&socid="+socid+"&fac_rec="+fac_rec;
3524 });
3525 });
3526 </script>';
3527 }
3528 print '</td></tr>';
3529 }
3530 $db->free($resql);
3531 } else {
3532 dol_print_error($db);
3533 }
3534 }
3535
3536 print '<tr><td class="tdtop fieldrequired">'.$langs->trans('Type').'</td><td colspan="2">';
3537 print '<div class="tagtable">'."\n";
3538
3539 // Standard invoice
3540 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3541 $tmp = '<input type="radio" id="radio_standard" name="type" value="0"'.(GETPOSTINT('type') ? '' : ' checked').'> ';
3542 $tmp = $tmp.'<label for="radio_standard" >'.$langs->trans("InvoiceStandardAsk").'</label>';
3543 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
3544 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceStandardDesc"), 1, 'help', 'nowraponall', 0, 3, 'standardonsmartphone');
3545 print '<table class="nobordernopadding"><tr>';
3546 print '<td>';
3547 print $desc;
3548 print '</td>';
3549 if ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid))) {
3550 /*print '<td class="nowrap" style="padding-left: 5px">';
3551 $arraylist = array(
3552 //'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
3553 //'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
3554 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
3555 );
3556 print $form->selectarray('typestandard', $arraylist, GETPOST('typestandard', 'aZ09'), 0, 0, 0, '', 1);
3557 print '</td>';*/
3558 print '<td class="nowrap" style="padding-left: 15px">';
3559 print '<span class="opacitymedium">'.$langs->trans('PercentOfOriginalObject').'</span>:<input class="right" placeholder="100%" type="text" id="valuestandardinvoice" name="valuestandardinvoice" size="3" value="'.(GETPOSTISSET('valuestandardinvoice') ? GETPOST('valuestandardinvoice', 'alpha') : '100%').'"/>';
3560 print '</td>';
3561 }
3562 print '</tr></table>';
3563 print '</div></div>';
3564
3565 if ((empty($origin)) || ((($origin == 'propal') || ($origin == 'commande')) && (!empty($originid)))) {
3566 // Deposit - Down payment
3567 if (!getDolGlobalString('INVOICE_DISABLE_DEPOSIT')) {
3568 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3569 $tmp = '<input type="radio" id="radio_deposit" name="type" value="3"'.(GETPOSTINT('type') == 3 ? ' checked' : '').'> ';
3570 print '<script type="text/javascript">
3571 jQuery(document).ready(function() {
3572 jQuery("#typestandardinvoice, #valuestandardinvoice").click(function() {
3573 jQuery("#radio_standard").prop("checked", true);
3574 });
3575 jQuery("#typedeposit, #valuedeposit").click(function() {
3576 jQuery("#radio_deposit").prop("checked", true);
3577 });
3578 jQuery("#typedeposit").change(function() {
3579 console.log("We change type of down payment");
3580 jQuery("#radio_deposit").prop("checked", true);
3581 setRadioForTypeOfInvoice();
3582 });
3583 jQuery("#radio_standard, #radio_deposit, #radio_replacement, #radio_creditnote, #radio_template").change(function() {
3584 setRadioForTypeOfInvoice();
3585 });
3586 function setRadioForTypeOfInvoice() {
3587 console.log("Change radio for type of invoice");
3588 if (jQuery("#radio_deposit").prop("checked") && (jQuery("#typedeposit").val() == \'amount\' || jQuery("#typedeposit").val() == \'variable\')) {
3589 jQuery("#checkforselects").prop("disabled", true);
3590 jQuery("#checkforselects").prop("checked", false);
3591 jQuery(".checkforselect").prop("disabled", true);
3592 jQuery(".checkforselect").prop("checked", false);
3593 } else {
3594 jQuery("#checkforselects").prop("disabled", false);
3595 jQuery("#checkforselects").prop("checked", true);
3596 jQuery(".checkforselect").prop("disabled", false);
3597 jQuery(".checkforselect").prop("checked", true);
3598 }
3599 }
3600 });
3601 </script>';
3602
3603 print '<table class="nobordernopadding"><tr>';
3604 print '<td>';
3605 $tmp = $tmp.'<label for="radio_deposit">'.$langs->trans("InvoiceDeposit").'</label>';
3606 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
3607 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceDepositDesc"), 1, 'help', '', 0, 3, 'depositonsmartphone');
3608 print $desc;
3609 print '</td>';
3610 if (($origin == 'propal') || ($origin == 'commande')) {
3611 print '<td class="nowrap" style="padding-left: 15px">';
3612 $arraylist = array(
3613 'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),
3614 'variable' => $langs->transnoentitiesnoconv('VarAmountOneLine', $langs->transnoentitiesnoconv('Deposit')),
3615 'variablealllines' => $langs->transnoentitiesnoconv('VarAmountAllLines')
3616 );
3617 $typedeposit = GETPOST('typedeposit', 'aZ09');
3618 $valuedeposit = GETPOSTINT('valuedeposit');
3619 if (empty($typedeposit) && !empty($objectsrc->deposit_percent)) {
3620 $origin_payment_conditions_deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $objectsrc->cond_reglement_id);
3621 if (!empty($origin_payment_conditions_deposit_percent)) {
3622 $typedeposit = 'variable';
3623 }
3624 }
3625 if (empty($valuedeposit) && $typedeposit == 'variable' && !empty($objectsrc->deposit_percent)) {
3626 $valuedeposit = $objectsrc->deposit_percent;
3627 }
3628 print $form->selectarray('typedeposit', $arraylist, $typedeposit, 0, 0, 0, '', 1);
3629 print '</td>';
3630 print '<td class="nowrap" style="padding-left: 5px">';
3631 print '<span class="opacitymedium paddingleft">'.$langs->trans("AmountOrPercent").'</span><input type="text" id="valuedeposit" name="valuedeposit" class="width75 right" value="'.$valuedeposit.'"/>';
3632 print '</td>';
3633 }
3634 print '</tr></table>';
3635
3636 print '</div></div>';
3637 }
3638 }
3639
3640 if ($socid > 0) {
3641 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
3642 // First situation invoice
3643 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3644 $tmp = '<input id="radio_situation" type="radio" name="type" value="5"'.(GETPOST('type') == 5 ? ' checked' : '').'> ';
3645 $tmp = $tmp.'<label for="radio_situation" >'.$langs->trans("InvoiceFirstSituationAsk").'</label>';
3646 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
3647 $desc = $form->textwithpicto($tmp, $langs->transnoentities("InvoiceFirstSituationDesc"), 1, 'help', '', 0, 3, 'firstsituationonsmartphone');
3648 print $desc;
3649 print '</div></div>';
3650
3651 // Next situation invoice
3652 $opt = $form->selectSituationInvoices(GETPOSTINT('originid'), $socid);
3653
3654 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3655 $tmp = '<input type="radio" name="type" value="5"'.(GETPOST('type') == 5 && GETPOSTINT('originid') ? ' checked' : '');
3656 if ($opt == ('<option value ="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
3657 $tmp .= ' disabled';
3658 }
3659 $tmp .= '> ';
3660 $text = $tmp.'<label>'.$langs->trans("InvoiceSituationAsk").'</label> ';
3661 $text .= '<select class="flat" id="situations" name="situations"';
3662 if ($opt == ('<option value ="0" selected>'.$langs->trans('NoSituations').'</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) {
3663 $text .= ' disabled';
3664 }
3665 $text .= '>';
3666 $text .= $opt;
3667 $text .= '</select>';
3668 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceSituationDesc"), 1, 'help', '', 0, 3);
3669 print $desc;
3670 print '</div></div>';
3671 }
3672
3673 // Replacement
3674 if (!getDolGlobalString('INVOICE_DISABLE_REPLACEMENT')) {
3675 // Type de facture
3676 $facids = $facturestatic->list_replacable_invoices($soc->id);
3677 if ($facids < 0) {
3678 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
3679 exit();
3680 }
3681 $options = "";
3682 if (is_array($facids)) {
3683 foreach ($facids as $facparam) {
3684 $options .= '<option value="'.$facparam ['id'].'"';
3685 if ($facparam['id'] == GETPOSTINT('fac_replacement')) {
3686 $options .= ' selected';
3687 }
3688 $options .= '>'.$facparam['ref'];
3689 $options .= ' ('.$facturestatic->LibStatut($facparam['paid'], $facparam['status'], 0, $facparam['alreadypaid']).')';
3690 $options .= '</option>';
3691 }
3692 }
3693
3694 print '<!-- replacement line -->';
3695 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3696 $tmp = '<input type="radio" name="type" id="radio_replacement" value="1"'.(GETPOST('type') == 1 ? ' checked' : '');
3697 if (!$options || $invoice_predefined->id > 0) {
3698 $tmp .= ' disabled';
3699 }
3700 $tmp .= '> ';
3701 print '<script type="text/javascript">
3702 jQuery(document).ready(function() {
3703 jQuery("#fac_replacement").change(function() {
3704 jQuery("#radio_replacement").prop("checked", true);
3705 });
3706 });
3707 </script>';
3708 $text = $tmp.'<label for="radio_replacement">'.$langs->trans("InvoiceReplacementAsk").'</label>';
3709 $text .= '<select class="flat" name="fac_replacement" id="fac_replacement"';
3710 if (!$options || $invoice_predefined->id > 0) {
3711 $text .= ' disabled';
3712 }
3713 $text .= '>';
3714 if ($options) {
3715 $text .= '<option value="-1">&nbsp;</option>';
3716 $text .= $options;
3717 } else {
3718 $text .= '<option value="-1">'.$langs->trans("NoReplacableInvoice").'</option>';
3719 }
3720 $text .= '</select>';
3721 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc"), 1, 'help', '', 0, 3);
3722 print $desc;
3723 print '</div></div>';
3724 }
3725 } else {
3726 if (getDolGlobalString('INVOICE_USE_SITUATION')) {
3727 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3728 $tmp = '<input type="radio" name="type" id="radio_situation" value="0" disabled> ';
3729 $text = $tmp.'<label>'.$langs->trans("InvoiceSituationAsk").'</label> ';
3730 $text .= '<span class="opacitymedium">('.$langs->trans("YouMustCreateInvoiceFromThird").')</span> ';
3731 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceFirstSituationDesc"), 1, 'help', 'nowraponall', 0, 3, 'firstsituationonsmartphone');
3732 print $desc;
3733 print '</div></div>';
3734 }
3735
3736 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3737 $tmp = '<input type="radio" name="type" id="radio_replacement" value="0" disabled> ';
3738 $text = $tmp.'<label for="radio_replacement" class="opacitymedium">'.$langs->trans("InvoiceReplacement").'</label> ';
3739 //$text .= '<span class="opacitymedium hideonsmartphone">('.$langs->trans("YouMustCreateInvoiceFromThird").')</span> ';
3740 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceReplacementDesc").'<br><br>'.$langs->trans("YouMustCreateInvoiceFromThird"), 1, 'help', 'nowraponall', 0, 3, 'replacementonsmartphone');
3741 print $desc;
3742 print '</div></div>';
3743 }
3744
3745 if (empty($origin)) {
3746 if ($socid > 0) {
3747 // Credit note
3748 if (!getDolGlobalString('INVOICE_DISABLE_CREDIT_NOTE')) {
3749 // Show link for credit note
3750 $facids = $facturestatic->list_qualified_avoir_invoices($soc->id);
3751 if ($facids < 0) {
3752 dol_print_error($db, $facturestatic->error, $facturestatic->errors);
3753 exit;
3754 }
3755 $optionsav = "";
3756 $newinvoice_static = new Facture($db);
3757 foreach ($facids as $key => $valarray) {
3758 $newinvoice_static->id = $key;
3759 $newinvoice_static->ref = $valarray ['ref'];
3760 $newinvoice_static->statut = $valarray ['status'];
3761 $newinvoice_static->status = $valarray ['status'];
3762 $newinvoice_static->type = $valarray ['type'];
3763 $newinvoice_static->paye = $valarray ['paye'];
3764
3765 $optionsav .= '<option value="'.$key.'"';
3766 if ($key == GETPOST('fac_avoir')) {
3767 $optionsav .= ' selected';
3768
3769 // pre-filled extra fields with selected credit note
3770 $newinvoice_static->fetch_optionals($key);
3771 $object->array_options = $newinvoice_static->array_options;
3772 }
3773 $optionsav .= '>';
3774 $optionsav .= $newinvoice_static->ref;
3775 $optionsav .= ' ('.$newinvoice_static->getLibStatut(1, $valarray ['paymentornot']).')';
3776 $optionsav .= '</option>';
3777 }
3778
3779 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3780 $tmp = '<input type="radio" id="radio_creditnote" name="type" value="2"'.(GETPOST('type') == 2 ? ' checked' : '');
3781 if ((!$optionsav && !getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) || $invoice_predefined->id > 0) {
3782 $tmp .= ' disabled';
3783 }
3784 $tmp .= '> ';
3785 // Show credit note options only if we checked credit note and disable standard invoice if "create credit note" button is pressed
3786 print '<script type="text/javascript">
3787 jQuery(document).ready(function() {
3788 if (jQuery("#radio_creditnote").is(":checked"))
3789 {
3790 jQuery("#radio_standard").prop("disabled", true);
3791 } else {
3792 jQuery("#radio_standard").prop("disabled", false);
3793 }
3794 if (! jQuery("#radio_creditnote").is(":checked"))
3795 {
3796 jQuery("#credit_note_options").hide();
3797 }
3798 jQuery("#radio_creditnote").click(function() {
3799 jQuery("#credit_note_options").show();
3800 });
3801 jQuery("#radio_standard, #radio_replacement, #radio_deposit").click(function() {
3802 jQuery("#credit_note_options").hide();
3803 });
3804 });
3805 </script>';
3806 $text = '<label>'.$tmp.$langs->transnoentities("InvoiceAvoirAsk").'</label> ';
3807 $text .= '<select class="flat valignmiddle" name="fac_avoir" id="fac_avoir"';
3808 if (!$optionsav || $invoice_predefined->id > 0) {
3809 $text .= ' disabled';
3810 }
3811 $text .= '>';
3812 if ($optionsav) {
3813 $text .= '<option value="-1"></option>';
3814 $text .= $optionsav;
3815 } else {
3816 $text .= '<option value="-1">'.$langs->trans("NoInvoiceToCorrect").'</option>';
3817 }
3818 $text .= '</select>';
3819 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3);
3820 print $desc;
3821
3822 print '<div id="credit_note_options" class="clearboth paddingtop marginbottomonly">';
3823 print '&nbsp;&nbsp;&nbsp; <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">'.$langs->trans('invoiceAvoirWithLines')."</label>";
3824 print '<br>&nbsp;&nbsp;&nbsp; <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">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label>";
3825 print '</div>';
3826
3827 print '</div></div>';
3828 }
3829 } else {
3830 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3831 if (!getDolGlobalString('INVOICE_CREDIT_NOTE_STANDALONE')) {
3832 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="0" disabled> ';
3833 } else {
3834 $tmp = '<input type="radio" name="type" id="radio_creditnote" value="2" > ';
3835 }
3836 $text = $tmp.'<label class="opacitymedium" for="radio_creditnote">'.$langs->trans("InvoiceAvoir").'</label> ';
3837 //$text .= '<span class="opacitymedium hideonsmartphone">('.$langs->trans("YouMustCreateInvoiceFromThird").')</span> ';
3838 $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc").'<br><br>'.$langs->trans("CreateCreditNoteWhenClientInvoiceExists"), 1, 'help', '', 0, 3, 'creditnoteonsmartphone');
3839 print $desc;
3840 print '</div></div>'."\n";
3841 }
3842 }
3843
3844 // Template invoice
3845 print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
3846 $tmp = '<input type="radio" name="type" id="radio_template" value="0" disabled> ';
3847 $text = $tmp.'<label class="opacitymedium" for="radio_template">'.$langs->trans("RepeatableInvoice").'</label> ';
3848 $desc = $form->textwithpicto($text, $langs->transnoentities("YouMustCreateStandardInvoiceFirstDesc"), 1, 'help', '', 0, 3, 'templateonsmartphone');
3849 print $desc;
3850 print '</div></div>';
3851
3852 print '</div>';
3853
3854
3855 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) { // Hidden conf
3856 // Add auto select default document model
3858 $jsListType = '';
3859 foreach ($listtType as $type) {
3860 $thisTypeConfName = 'FACTURE_ADDON_PDF_'.$type;
3861 $current = getDolGlobalString($thisTypeConfName, getDolGlobalString('FACTURE_ADDON_PDF'));
3862 $jsListType .= (!empty($jsListType) ? ',' : '').'"'.$type.'":"'.$current.'"';
3863 }
3864
3865 print '<script type="text/javascript">
3866 $(document).ready(function() {
3867 var listType = {'.$jsListType.'};
3868 $("[name=\'type\'").change(function() {
3869 console.log("change name=type");
3870 if ($( this ).prop("checked"))
3871 {
3872 if(($( this ).val() in listType))
3873 {
3874 $("#model").val(listType[$( this ).val()]);
3875 }
3876 else
3877 {
3878 $("#model").val("' . getDolGlobalString('FACTURE_ADDON_PDF').'");
3879 }
3880 }
3881 });
3882 });
3883 </script>';
3884 }
3885
3886
3887 print '</td></tr>';
3888
3889 // Invoice Subtype
3890 if (getDolGlobalInt('INVOICE_SUBTYPE_ENABLED')) {
3891 print '<tr><td class="fieldrequired">'.$langs->trans('InvoiceSubtype').'</td><td colspan="2">';
3892 print $form->getSelectInvoiceSubtype(GETPOST('subtype'), 'subtype', 1, 0, '');
3893 print '</td></tr>';
3894 }
3895
3896 // Discounts for the known third party
3897 if ($socid > 0) {
3898 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td><td colspan="2">';
3899
3900 $thirdparty = $soc; // used by object_discounts.tpl.php
3901 $discount_type = 0; // used by object_discounts.tpl.php
3902 $backtopage = $_SERVER["PHP_SELF"].'?socid='.$thirdparty->id.'&action='.$action.'&origin='.urlencode((string) (GETPOST('origin'))).'&originid='.urlencode((string) (GETPOSTINT('originid')));
3903 include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php';
3904
3905 print '</td></tr>';
3906 }
3907
3908 $newdateinvoice = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
3909 $date_pointoftax = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'), 'tzserver');
3910
3911 // Date invoice
3912 print '<tr><td class="fieldrequired">'.$langs->trans('DateInvoice').'</td><td colspan="2">';
3913 print img_picto('', 'action', 'class="pictofixedwidth"');
3914 print $form->selectDate($newdateinvoice ? $newdateinvoice : $dateinvoice, '', 0, 0, 0, "add", 1, 1);
3915 print '</td></tr>';
3916
3917 // Date point of tax
3918 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
3919 print '<tr><td class="fieldrequired">'.$langs->trans('DatePointOfTax').'</td><td colspan="2">';
3920 print img_picto('', 'action', 'class="pictofixedwidth"');
3921 print $form->selectDate($date_pointoftax ? $date_pointoftax : -1, 'date_pointoftax', 0, 0, 0, "add", 1, 1);
3922 print '</td></tr>';
3923 }
3924
3925 // Payment term
3926 print '<tr><td class="nowrap fieldrequired">'.$langs->trans('PaymentConditionsShort').'</td><td colspan="2">';
3927 print img_picto('', 'payment', 'class="pictofixedwidth"');
3928 print $form->getSelectConditionsPaiements($cond_reglement_id, 'cond_reglement_id', -1, 1, 0, 'maxwidth500 widthcentpercentminusx');
3929 print '</td></tr>';
3930
3931 // Warranty
3932 if (getDolGlobalString('INVOICE_USE_RETAINED_WARRANTY')) {
3933 $rwStyle = 'display:none;';
3934 if (in_array(GETPOSTINT('type'), $retainedWarrantyInvoiceAvailableType)) {
3935 $rwStyle = '';
3936 }
3937
3938 $retained_warranty = GETPOSTINT('retained_warranty');
3939 if (empty($retained_warranty)) {
3940 if (!empty($objectsrc->retained_warranty)) { // use previous situation value
3941 $retained_warranty = $objectsrc->retained_warranty;
3942 }
3943 }
3944 $retained_warranty_js_default = !empty($retained_warranty) ? $retained_warranty : getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_PERCENT');
3945
3946 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('RetainedWarranty').'</td><td colspan="2">';
3947 print '<input id="new-situation-invoice-retained-warranty" name="retained_warranty" type="number" value="'.$retained_warranty.'" step="0.01" min="0" max="100" />%';
3948
3949 // Retained warranty payment term
3950 print '<tr class="retained-warranty-line" style="'.$rwStyle.'" ><td class="nowrap">'.$langs->trans('PaymentConditionsShortRetainedWarranty').'</td><td colspan="2">';
3951 $retained_warranty_fk_cond_reglement = GETPOSTINT('retained_warranty_fk_cond_reglement');
3952 if (empty($retained_warranty_fk_cond_reglement)) {
3953 $retained_warranty_fk_cond_reglement = getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
3954 if (!empty($objectsrc->retained_warranty_fk_cond_reglement)) { // use previous situation value
3955 $retained_warranty_fk_cond_reglement = $objectsrc->retained_warranty_fk_cond_reglement;
3956 } else {
3957 $retained_warranty_fk_cond_reglement = getDolGlobalString('INVOICE_SITUATION_DEFAULT_RETAINED_WARRANTY_COND_ID');
3958 }
3959 }
3960 print $form->getSelectConditionsPaiements($retained_warranty_fk_cond_reglement, 'retained_warranty_fk_cond_reglement', -1, 1);
3961 print '</td></tr>';
3962
3963 print '<script type="text/javascript">
3964 $(document).ready(function() {
3965 $("[name=\'type\']").change(function() {
3966 if($( this ).prop("checked") && $.inArray($( this ).val(), '.json_encode($retainedWarrantyInvoiceAvailableType).' ) !== -1)
3967 {
3968 $(".retained-warranty-line").show();
3969 $("#new-situation-invoice-retained-warranty").val("'.(float) $retained_warranty_js_default.'");
3970 }
3971 else{
3972 $(".retained-warranty-line").hide();
3973 $("#new-situation-invoice-retained-warranty").val("");
3974 }
3975 });
3976
3977 $("[name=\'type\']:checked").trigger("change");
3978 });
3979 </script>';
3980 }
3981
3982 // Payment mode
3983 print '<tr><td>'.$langs->trans('PaymentMode').'</td><td colspan="2">';
3984 print img_picto('', 'bank', 'class="pictofixedwidth"');
3985 print $form->select_types_paiements($mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth200 widthcentpercentminusx', 1);
3986 print '</td></tr>';
3987
3988 // Bank Account
3989 if (isModEnabled("bank")) {
3990 print '<tr><td>'.$langs->trans('BankAccount').'</td><td colspan="2">';
3991 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
3992 print $form->select_comptes($fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth200 widthcentpercentminusx', 1);
3993 print '</td></tr>';
3994 }
3995
3996 // Project
3997 if (isModEnabled('project')) {
3998 $langs->load('projects');
3999 print '<tr><td>'.$langs->trans('Project').'</td><td colspan="2">';
4000 print img_picto('', 'project', 'class="pictofixedwidth"').$formproject->select_projects(($socid > 0 ? $socid : -1), $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500 widthcentpercentminusxx');
4001 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>';
4002 print '</td></tr>';
4003 }
4004
4005 // Incoterms
4006 if (isModEnabled('incoterm')) {
4007 print '<tr>';
4008 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), !empty($objectsrc->label_incoterms) ? $objectsrc->label_incoterms : '', 1).'</label></td>';
4009 print '<td colspan="2" class="maxwidthonsmartphone">';
4010 $incoterm_id = GETPOST('incoterm_id');
4011 $location_incoterms = GETPOST('location_incoterms');
4012 if (empty($incoterm_id)) {
4013 $incoterm_id = (!empty($objectsrc->fk_incoterms) ? $objectsrc->fk_incoterms : $soc->fk_incoterms);
4014 $location_incoterms = (!empty($objectsrc->location_incoterms) ? $objectsrc->location_incoterms : $soc->location_incoterms);
4015 }
4016 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
4017 print $form->select_incoterms($incoterm_id, $location_incoterms);
4018 print '</td></tr>';
4019 }
4020
4021 // Other attributes
4022 $parameters = array('objectsrc' => !empty($objectsrc) ? $objectsrc : 0, 'colspan' => ' colspan="2"', 'cols' => '2', 'socid' => $socid);
4023 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
4024 print $hookmanager->resPrint;
4025 if (empty($reshook)) {
4026 if (getDolGlobalString('THIRDPARTY_PROPAGATE_EXTRAFIELDS_TO_INVOICE') && !empty($soc->id)) {
4027 // copy from thirdparty
4028 $tpExtrafields = new ExtraFields($db);
4029 $tpExtrafieldLabels = $tpExtrafields->fetch_name_optionals_label($soc->table_element);
4030 if ($soc->fetch_optionals() > 0) {
4031 $object->array_options = array_merge($object->array_options, $soc->array_options);
4032 }
4033 }
4034
4035 print $object->showOptionals($extrafields, 'create', $parameters);
4036 }
4037
4038 // Template to use by default
4039 print '<tr><td>'.$langs->trans('Model').'</td>';
4040 print '<td colspan="2">';
4041 print img_picto('', 'pdf', 'class="pictofixedwidth"');
4042 include_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
4044 if (getDolGlobalString('INVOICE_USE_DEFAULT_DOCUMENT')) {
4045 // Hidden conf
4046 $paramkey = 'FACTURE_ADDON_PDF_'.$object->type;
4047 $preselected = getDolGlobalString($paramkey, getDolGlobalString('FACTURE_ADDON_PDF'));
4048 } else {
4049 $preselected = getDolGlobalString('FACTURE_ADDON_PDF');
4050 }
4051 print $form->selectarray('model', $liste, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
4052 print "</td></tr>";
4053
4054 // Multicurrency
4055 if (isModEnabled('multicurrency')) {
4056 print '<tr>';
4057 print '<td>'.$form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0).'</td>';
4058 print '<td colspan="2" class="maxwidthonsmartphone">';
4059 print img_picto('', 'currency', 'class="pictofixedwidth"');
4060 print $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth100 widthcentpercentminusx');
4061 print '</td></tr>';
4062 }
4063
4064 // Help of substitution key
4065 $htmltext = '';
4066 if (GETPOSTINT('fac_rec') > 0) {
4067 $dateexample = ($newdateinvoice ? $newdateinvoice : $dateinvoice);
4068 if (empty($dateexample)) {
4069 $dateexample = dol_now();
4070 }
4071 $substitutionarray = array(
4072 '__TOTAL_HT__' => $langs->trans("AmountHT").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ht).')',
4073 '__TOTAL_TTC__' => $langs->trans("AmountTTC").' ('.$langs->trans("Example").': '.price($exampletemplateinvoice->total_ttc).')',
4074 '__INVOICE_PREVIOUS_MONTH__' => $langs->trans("PreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%m').')',
4075 '__INVOICE_MONTH__' => $langs->trans("MonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%m').')',
4076 '__INVOICE_NEXT_MONTH__' => $langs->trans("NextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%m').')',
4077 '__INVOICE_PREVIOUS_MONTH_TEXT__' => $langs->trans("TextPreviousMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'm'), '%B').')',
4078 '__INVOICE_MONTH_TEXT__' => $langs->trans("TextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%B').')',
4079 '__INVOICE_NEXT_MONTH_TEXT__' => $langs->trans("TextNextMonthOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'm'), '%B').')',
4080 '__INVOICE_PREVIOUS_YEAR__' => $langs->trans("PreviousYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, -1, 'y'), '%Y').')',
4081 '__INVOICE_YEAR__' => $langs->trans("YearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample, '%Y').')',
4082 '__INVOICE_NEXT_YEAR__' => $langs->trans("NextYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'y'), '%Y').')'
4083 );
4084
4085 $htmltext = '<i>'.$langs->trans("FollowingConstantsWillBeSubstituted").':<br>';
4086 foreach ($substitutionarray as $key => $val) {
4087 $htmltext .= $key.' = '.$langs->trans($val).'<br>';
4088 }
4089 $htmltext .= '</i>';
4090 }
4091
4092 // Public note
4093 print '<tr>';
4094 print '<td class="tdtop">';
4095 print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
4096 print '</td>';
4097 print '<td valign="top" colspan="2">';
4098 $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
4099 print $doleditor->Create(1);
4100
4101 // Private note
4102 if (empty($user->socid)) {
4103 print '<tr>';
4104 print '<td class="tdtop">';
4105 print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
4106 print '</td>';
4107 print '<td valign="top" colspan="2">';
4108 $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
4109 print $doleditor->Create(1);
4110 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
4111 print '</td></tr>';
4112 }
4113
4114 // Lines from source (TODO Show them also when creating invoice from template invoice)
4115 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
4116 $langs->loadLangs(array('orders', 'propal'));
4117
4118 // TODO for compatibility
4119 if ($origin == 'contrat') {
4120 // Calcul contrat->price (HT), contrat->total (TTC), contrat->tva
4121 //$objectsrc->remise_absolue = $remise_absolue;
4122 //$objectsrc->remise_percent = $remise_percent;
4123 $objectsrc->update_price(1, 'auto', 1);
4124 }
4125
4126 print "\n<!-- Show ref of origin ".$classname." -->\n";
4127 print '<input type="hidden" name="amount" value="'.$objectsrc->total_ht.'">'."\n";
4128 print '<input type="hidden" name="total" value="'.$objectsrc->total_ttc.'">'."\n";
4129 print '<input type="hidden" name="tva" value="'.$objectsrc->total_tva.'">'."\n";
4130 // The commented lines below are fields already added as hidden parameters before
4131 //print '<input type="hidden" name="origin" value="'.$objectsrc->element.'">';
4132 //print '<input type="hidden" name="originid" value="'.$objectsrc->id.'">';
4133
4134 switch (get_class($objectsrc)) {
4135 case 'Propal':
4136 $newclassname = 'CommercialProposal';
4137 break;
4138 case 'Commande':
4139 $newclassname = 'Order';
4140 break;
4141 case 'Expedition':
4142 $newclassname = 'Sending';
4143 break;
4144 case 'Contrat':
4145 $newclassname = 'Contract';
4146 break;
4147 case 'Fichinter':
4148 $newclassname = 'Intervention';
4149 break;
4150 default:
4151 $newclassname = get_class($objectsrc);
4152 }
4153
4154 // Ref of origin
4155 print '<tr><td>'.$langs->trans($newclassname).'</td>';
4156 print '<td colspan="2">';
4157 print $objectsrc->getNomUrl(1);
4158 // We check if Origin document (id and type is known) has already at least one invoice attached to it
4159 $objectsrc->fetchObjectLinked($originid, $origin, null, 'facture');
4160 if (isset($objectsrc->linkedObjects['facture']) && is_array($objectsrc->linkedObjects['facture']) && count($objectsrc->linkedObjects['facture']) >= 1) {
4161 setEventMessages('WarningBillExist', null, 'warnings');
4162 echo ' - '.$langs->trans('LatestRelatedBill').' '.end($objectsrc->linkedObjects['facture'])->getNomUrl(1);
4163 }
4164 echo '</td></tr>';
4165 print '<tr><td>'.$langs->trans('AmountHT').'</td><td colspan="2">'.price($objectsrc->total_ht).'</td></tr>';
4166 print '<tr><td>'.$langs->trans('AmountVAT').'</td><td colspan="2">'.price($objectsrc->total_tva)."</td></tr>";
4167 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
4168 print '<tr><td>'.$langs->transcountry("AmountLT1", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax1)."</td></tr>";
4169 }
4170
4171 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
4172 print '<tr><td>'.$langs->transcountry("AmountLT2", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax2)."</td></tr>";
4173 }
4174 print '<tr><td>'.$langs->trans('AmountTTC').'</td><td colspan="2">'.price($objectsrc->total_ttc)."</td></tr>";
4175
4176 if (isModEnabled('multicurrency')) {
4177 print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ht).'</td></tr>';
4178 print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_tva)."</td></tr>";
4179 print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ttc)."</td></tr>";
4180 }
4181 }
4182
4183 print "</table>\n";
4184 }
4185 print dol_get_fiche_end();
4186
4187 print $form->buttonsSaveCancel("CreateDraft");
4188
4189 // Show origin lines
4190 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
4191 print '<br>';
4192
4193 $title = $langs->trans('ProductsAndServices');
4194 print load_fiche_titre($title);
4195
4196 print '<div class="div-table-responsive-no-min">';
4197 print '<table class="noborder centpercent">';
4198
4199 $objectsrc->printOriginLinesList('', $selectedLines);
4200
4201 print '</table>';
4202 print '</div>';
4203 }
4204
4205 print "</form>\n";
4206} elseif ($id > 0 || !empty($ref)) {
4207 if (empty($object->id)) {
4208 $langs->load('errors');
4209 echo '<div class="error">'.$langs->trans("ErrorRecordNotFound").'</div>';
4210 llxFooter();
4211 exit;
4212 }
4213
4214 /*
4215 * Show object in view mode
4216 */
4217
4218 $result = $object->fetch($id, $ref);
4219 if ($result <= 0) {
4220 dol_print_error($db, $object->error, $object->errors);
4221 exit();
4222 }
4223
4224 // fetch optionals attributes and labels
4225 $extrafields->fetch_name_optionals_label($object->table_element);
4226
4227 if ($user->socid > 0 && $user->socid != $object->socid) {
4228 accessforbidden('', 0, 1);
4229 }
4230
4231 $result = $object->fetch_thirdparty();
4232
4233 $result = $soc->fetch($object->socid);
4234 if ($result < 0) {
4235 dol_print_error($db);
4236 }
4237 $selleruserevenustamp = $mysoc->useRevenueStamp();
4238
4239 $totalpaid = $object->getSommePaiement();
4240 $totalcreditnotes = $object->getSumCreditNotesUsed();
4241 $totaldeposits = $object->getSumDepositsUsed();
4242 //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits."
4243 // selleruserrevenuestamp=".$selleruserevenustamp;
4244
4245 // We can also use bcadd to avoid pb with floating points
4246 // For example print 239.2 - 229.3 - 9.9; does not return 0.
4247 $resteapayer = price2num($object->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
4248
4249 // Multicurrency
4250 if (isModEnabled('multicurrency')) {
4251 $multicurrency_totalpaid = $object->getSommePaiement(1);
4252 $multicurrency_totalcreditnotes = $object->getSumCreditNotesUsed(1);
4253 $multicurrency_totaldeposits = $object->getSumDepositsUsed(1);
4254 $multicurrency_resteapayer = price2num($object->multicurrency_total_ttc - $multicurrency_totalpaid - $multicurrency_totalcreditnotes - $multicurrency_totaldeposits, 'MT');
4255 // Code to fix case of corrupted data
4256 // 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
4257 // 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)
4258 if ($resteapayer == 0 && $multicurrency_resteapayer != 0 && $object->multicurrency_code != $conf->currency) {
4259 $resteapayer = price2num((float) $multicurrency_resteapayer / $object->multicurrency_tx, 'MT');
4260 }
4261 }
4262
4263 if ($object->paye) {
4264 $resteapayer = 0;
4265 }
4266 $resteapayeraffiche = $resteapayer;
4267
4268 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) { // Never use this
4269 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
4270 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
4271 } else {
4272 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
4273 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
4274 }
4275
4276 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
4277 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
4278 $absolute_discount = price2num($absolute_discount, 'MT');
4279 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
4280
4281 $author = new User($db);
4282 if ($object->user_creation_id) {
4283 $author->fetch($object->user_creation_id);
4284 }
4285
4286 $objectidnext = $object->getIdReplacingInvoice();
4287
4289
4290 print dol_get_fiche_head($head, 'compta', $langs->trans('InvoiceCustomer'), -1, 'bill');
4291
4292 $formconfirm = '';
4293
4294 // Confirmation of the conversion of the credit into a reduction
4295 if ($action == 'converttoreduc') {
4297 $type_fac = 'ExcessReceived';
4298 } elseif ($object->type == Facture::TYPE_CREDIT_NOTE) {
4299 $type_fac = 'CreditNote';
4300 } elseif ($object->type == Facture::TYPE_DEPOSIT) {
4301 $type_fac = 'Deposit';
4302 }
4303 $text = $langs->trans('ConfirmConvertToReduc', strtolower($langs->transnoentities($type_fac)));
4304 $text .= '<br>'.$langs->trans('ConfirmConvertToReduc2');
4305 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('ConvertToReduc'), $text, 'confirm_converttoreduc', '', "yes", 2);
4306 }
4307
4308 // Confirmation to delete invoice
4309 if ($action == 'delete') {
4310 $text = $langs->trans('ConfirmDeleteBill', $object->ref);
4311 $formquestion = array();
4312
4313 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL') && $object->status >= 1) {
4314 $qualified_for_stock_change = 0;
4315 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
4316 $qualified_for_stock_change = $object->hasProductsOrServices(2);
4317 } else {
4318 $qualified_for_stock_change = $object->hasProductsOrServices(1);
4319 }
4320
4321 if ($qualified_for_stock_change) {
4322 $langs->load("stocks");
4323 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
4324 $formproduct = new FormProduct($db);
4325 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
4326 $forcecombo = 0;
4327 if ($conf->browser->name == 'ie') {
4328 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
4329 }
4330 $formquestion = array(
4331 // 'text' => $langs->trans("ConfirmClone"),
4332 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
4333 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
4334 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1, 0, 0, $langs->trans("NoStockAction"), 0, $forcecombo))
4335 );
4336 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', $formquestion, "yes", 1);
4337 } else {
4338 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
4339 }
4340 } else {
4341 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1);
4342 }
4343 }
4344
4345 // Confirmation to remove invoice from cycle
4346 if ($action == 'situationout') {
4347 $text = $langs->trans('ConfirmRemoveSituationFromCycle', $object->ref);
4348 $label = $langs->trans("ConfirmOuting");
4349 $formquestion = array();
4350 // remove situation from cycle
4351 if (in_array($object->status, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED))
4352 && $usercancreate
4353 && !$objectidnext
4354 && $object->is_last_in_cycle()
4355 && $usercanunvalidate
4356 ) {
4357 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $label, $text, 'confirm_situationout', $formquestion, "yes", 1);
4358 }
4359 }
4360
4361 // Confirmation of validation
4362 if ($action == 'valid') {
4363 // we check object has a draft number
4364 $objectref = substr($object->ref, 1, 4);
4365 if ($objectref == 'PROV') {
4366 $savdate = $object->date;
4367 if (getDolGlobalString('FAC_FORCE_DATE_VALIDATION')) {
4368 $object->date = dol_now();
4369 $object->date_lim_reglement = $object->calculate_date_lim_reglement();
4370 }
4371 $numref = $object->getNextNumRef($soc);
4372 // $object->date=$savdate;
4373 } else {
4374 $numref = $object->ref;
4375 }
4376
4377 $text = $langs->trans('ConfirmValidateBill', $numref);
4378 if (isModEnabled('notification')) {
4379 require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
4380 $notify = new Notify($db);
4381 $text .= '<br>';
4382 $text .= $notify->confirmMessage('BILL_VALIDATE', $object->socid, $object);
4383 }
4384 $formquestion = array();
4385
4386 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
4387 $qualified_for_stock_change = 0;
4388 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
4389 $qualified_for_stock_change = $object->hasProductsOrServices(2);
4390 } else {
4391 $qualified_for_stock_change = $object->hasProductsOrServices(1);
4392 }
4393
4394 if ($qualified_for_stock_change) {
4395 $langs->load("stocks");
4396 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
4397 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
4398 $formproduct = new FormProduct($db);
4399 $warehouse = new Entrepot($db);
4400 $warehouse_array = $warehouse->list_array();
4401 if (count($warehouse_array) == 1) {
4402 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockIncrease", current($warehouse_array)) : $langs->trans("WarehouseForStockDecrease", current($warehouse_array));
4403 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
4404 } else {
4405 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockIncrease") : $langs->trans("SelectWarehouseForStockDecrease");
4406 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
4407 }
4408 $formquestion = array(
4409 // 'text' => $langs->trans("ConfirmClone"),
4410 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
4411 // 1),
4412 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
4413 // => 1),
4414 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
4415 }
4416 }
4417 if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) { // Can happen only if getDolGlobalString('FACTURE_ENABLE_NEGATIVE') is on
4418 $text .= '<br>'.img_warning().' '.$langs->trans("ErrorInvoiceOfThisTypeMustBePositive");
4419 }
4420
4421 // mandatoryPeriod
4422 $nbMandated = 0;
4423 foreach ($object->lines as $line) {
4424 $res = $line->fetch_product();
4425 if ($res > 0) {
4426 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
4427 $nbMandated++;
4428 break;
4429 }
4430 }
4431 }
4432 if ($nbMandated > 0) {
4433 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
4434 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
4435 $error++;
4436 } else {
4437 $text .= '<div><span class="clearboth nowraponall warning">'.img_warning().$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
4438 }
4439 }
4440
4441 if (!$error) {
4442 $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, 240);
4443 }
4444 }
4445
4446 // Confirm back to draft status
4447 if ($action == 'modif') {
4448 $text = $langs->trans('ConfirmUnvalidateBill', $object->ref);
4449 $formquestion = array();
4450
4451 if ($object->type != Facture::TYPE_DEPOSIT && getDolGlobalString('STOCK_CALCULATE_ON_BILL')) {
4452 $qualified_for_stock_change = 0;
4453 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
4454 $qualified_for_stock_change = $object->hasProductsOrServices(2);
4455 } else {
4456 $qualified_for_stock_change = $object->hasProductsOrServices(1);
4457 }
4458
4459 if ($qualified_for_stock_change) {
4460 $langs->load("stocks");
4461 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
4462 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
4463 $formproduct = new FormProduct($db);
4464 $warehouse = new Entrepot($db);
4465 $warehouse_array = $warehouse->list_array();
4466 if (count($warehouse_array) == 1) {
4467 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("WarehouseForStockDecrease", current($warehouse_array)) : $langs->trans("WarehouseForStockIncrease", current($warehouse_array));
4468 $value = '<input type="hidden" id="idwarehouse" name="idwarehouse" value="'.key($warehouse_array).'">';
4469 } else {
4470 $label = $object->type == Facture::TYPE_CREDIT_NOTE ? $langs->trans("SelectWarehouseForStockDecrease") : $langs->trans("SelectWarehouseForStockIncrease");
4471 $value = $formproduct->selectWarehouses(GETPOST('idwarehouse') ? GETPOST('idwarehouse') : 'ifone', 'idwarehouse', '', 1);
4472 }
4473 $formquestion = array(
4474 // 'text' => $langs->trans("ConfirmClone"),
4475 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' =>
4476 // 1),
4477 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value'
4478 // => 1),
4479 array('type' => 'other', 'name' => 'idwarehouse', 'label' => $label, 'value' => $value));
4480 }
4481 }
4482
4483 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('UnvalidateBill'), $text, 'confirm_modif', $formquestion, "yes", 1);
4484 }
4485
4486 // Confirmation of payment classification
4487 if ($action == 'paid' && ($resteapayer <= 0 || (getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') && $resteapayer == $object->total_ttc))) {
4488 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidBill', $object->ref), 'confirm_paid', '', "yes", 1);
4489 }
4490 if ($action == 'paid' && $resteapayer > 0 && (!getDolGlobalString('INVOICE_CAN_SET_PAID_EVEN_IF_PARTIALLY_PAID') || $resteapayer != $object->total_ttc)) {
4491 $close = array();
4492 // Code
4493 $i = 0;
4494 $close[$i]['code'] = 'discount_vat'; // escompte
4495 $i++;
4496 $close[$i]['code'] = 'badcustomer';
4497 $i++;
4498 $close[$i]['code'] = 'bankcharge';
4499 $i++;
4500 $close[$i]['code'] = 'withholdingtax';
4501 $i++;
4502 $close[$i]['code'] = 'other';
4503 $i++;
4504 // Help
4505 $i = 0;
4506 $close[$i]['label'] = $langs->trans("HelpEscompte").'<br><br>'.$langs->trans("ConfirmClassifyPaidPartiallyReasonDiscountVatDesc");
4507 $i++;
4508 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
4509 $i++;
4510 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBankChargeDesc");
4511 $i++;
4512 $close[$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonWithholdingTaxDesc");
4513 $i++;
4514 $close[$i]['label'] = $langs->trans("Other");
4515 $i++;
4516 // Texte
4517 $i = 0;
4518 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonDiscount", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
4519 $i++;
4520 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
4521 $i++;
4522 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBankCharge", $resteapayer, $langs->trans("Currency".$conf->currency)), $close[$i]['label'], 1);
4523 $i++;
4524 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonWithholdingTax"), $close[$i]['label'], 1);
4525 $i++;
4526 $close[$i]['reason'] = $form->textwithpicto($langs->transnoentities("Other"), $close[$i]['label'], 1);
4527 $i++;
4528 // arrayreasons[code]=reason
4529 $arrayreasons = [];
4530 foreach ($close as $key => $val) {
4531 $arrayreasons[$close[$key]['code']] = $close[$key]['reason'];
4532 }
4533
4534 // Create a form table
4535 $formquestion = array('text' => $langs->trans("ConfirmClassifyPaidPartiallyQuestion"), 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'));
4536 // Incomplete payment. We ask if reason = discount or other
4537 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidPartially', $object->ref), 'confirm_paid_partially', $formquestion, "yes", 1, 380, 600);
4538 }
4539
4540 // Confirmation of status abandoned
4541 if ($action == 'canceled') {
4542 // If there is a replacement invoice not yet validated (draft state),
4543 // it is not allowed to classify the invoice as abandoned.
4544 if ($objectidnext) {
4545 $facturereplacement = new Facture($db);
4546 $facturereplacement->fetch($objectidnext);
4547 $statusreplacement = $facturereplacement->status;
4548 }
4549 if ($objectidnext && $statusreplacement == 0) {
4550 print '<div class="error">'.$langs->trans("ErrorCantCancelIfReplacementInvoiceNotValidated").'</div>';
4551 } else {
4552 // Code
4553 $close[1]['code'] = 'badcustomer';
4554 $close[2]['code'] = 'abandon';
4555 // Help
4556 $close[1]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc");
4557 $close[2]['label'] = $langs->trans("ConfirmClassifyAbandonReasonOtherDesc");
4558 // Text
4559 $close[1]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $object->ref), $close[1]['label'], 1);
4560 $close[2]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyAbandonReasonOther"), $close[2]['label'], 1);
4561 // arrayreasons
4562 $arrayreasons = [];
4563 $arrayreasons[$close[1]['code']] = $close[1]['reason'];
4564 $arrayreasons[$close[2]['code']] = $close[2]['reason'];
4565
4566 // Create a form table
4567 $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'));
4568
4569 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$object->id, $langs->trans('CancelBill'), $langs->trans('ConfirmCancelBill', $object->ref), 'confirm_canceled', $formquestion, "yes", 1, 270);
4570 }
4571 }
4572
4573 if ($action == 'deletepayment') {
4574 $payment_id = GETPOST('paiement_id');
4575 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&paiement_id='.$payment_id, $langs->trans('DeletePayment'), $langs->trans('ConfirmDeletePayment'), 'confirm_delete_paiement', '', 'no', 1);
4576 }
4577
4578 // Confirmation de la suppression d'une ligne produit
4579 if ($action == 'ask_deleteline') {
4580 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 'no', 1);
4581 }
4582
4583 // Clone confirmation
4584 if ($action == 'clone') {
4585 $filter = '(s.client:IN:1,2,3)';
4586 // Create an array for form
4587 $formquestion = array(
4588 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company($object->socid, 'socid', $filter, 1)),
4589 array('type' => 'date', 'name' => 'newdate', 'label' => $langs->trans("Date"), 'value' => dol_now())
4590 );
4591 // Request confirmation to clone
4592 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneInvoice', $object->ref), 'confirm_clone', $formquestion, 'yes', 1, 250);
4593 }
4594
4595 if ($action == "remove_file_comfirm") {
4596 $file = GETPOST('file', 'alpha');
4597
4598 $formconfirm = $form->formconfirm(
4599 $_SERVER["PHP_SELF"].'?facid='.$object->id.'&file='.urlencode($file),
4600 $langs->trans('DeleteFileHeader'),
4601 $langs->trans('DeleteFileText')."<br><br>".$file,
4602 'remove_file',
4603 '',
4604 'no',
4605 1
4606 );
4607 }
4608
4609 // Call Hook formConfirm
4610 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid, 'remainingtopay' => &$resteapayer);
4611 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
4612 if (empty($reshook)) {
4613 $formconfirm .= $hookmanager->resPrint;
4614 } elseif ($reshook > 0) {
4615 $formconfirm = $hookmanager->resPrint;
4616 }
4617
4618 // Print form confirm
4619 print $formconfirm;
4620
4621 // Invoice content
4622
4623 $linkback = '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
4624
4625 $morehtmlref = '<div class="refidno">';
4626 // Ref invoice
4627 if ($object->status == $object::STATUS_DRAFT && !$mysoc->isInEEC() && getDolGlobalString('INVOICE_ALLOW_FREE_REF')) {
4628 $morehtmlref .= $form->editfieldkey("Ref", 'ref', $object->ref, $object, $usercancreate, 'string', '', 0, 1);
4629 $morehtmlref .= $form->editfieldval("Ref", 'ref', $object->ref, $object, $usercancreate, 'string', '', null, null, '', 1);
4630 $morehtmlref .= '<br>';
4631 }
4632 // Ref customer
4633 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_customer, $object, $usercancreate, 'string', '', 0, 1);
4634 $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_client', $object->ref_customer, $object, $usercancreate, 'string'.(getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
4635 // Thirdparty
4636 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1, 'customer');
4637 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
4638 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$object->thirdparty->id.'&search_societe='.urlencode($object->thirdparty->name).'">'.$langs->trans("OtherBills").'</a>)';
4639 }
4640 // Project
4641 if (isModEnabled('project')) {
4642 $langs->load("projects");
4643 $morehtmlref .= '<br>';
4644 if ($usercancreate) {
4645 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
4646 if ($action != 'classify') {
4647 $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
4648 }
4649 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
4650 } else {
4651 if (!empty($object->fk_project)) {
4652 $proj = new Project($db);
4653 $proj->fetch($object->fk_project);
4654 $morehtmlref .= $proj->getNomUrl(1);
4655 if ($proj->title) {
4656 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
4657 }
4658 }
4659 }
4660 }
4661 $morehtmlref .= '</div>';
4662
4663 $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status
4664
4665 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref, '', 0, '', '');
4666
4667 // Call Hook tabContentViewInvoice
4668 $parameters = array();
4669 // Note that $action and $object may be modified by hook
4670 $reshook = $hookmanager->executeHooks('tabContentViewInvoice', $parameters, $object, $action);
4671 if (empty($reshook)) {
4672 print '<div class="fichecenter">';
4673 print '<div class="fichehalfleft">';
4674 print '<div class="underbanner clearboth"></div>';
4675
4676 print '<table class="border centpercent tableforfield">';
4677
4678 // Type
4679 print '<tr><td class="titlefield fieldname_type">'.$langs->trans('Type').'</td><td class="valuefield fieldname_type">';
4680 print $object->getLibType(2);
4681 if ($object->subtype > 0) {
4682 print ' '.$object->getSubtypeLabel('facture');
4683 }
4684 if ($object->module_source) {
4685 print ' <span class="opacitymediumbycolor paddingleft">('.$langs->trans("POS").' '.dol_escape_htmltag(ucfirst($object->module_source)).' - '.$langs->trans("Terminal").' '.dol_escape_htmltag($object->pos_source).')</span>';
4686 }
4687 if ($object->type == Facture::TYPE_REPLACEMENT) {
4688 $facreplaced = new Facture($db);
4689 $facreplaced->fetch($object->fk_facture_source);
4690 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("ReplaceInvoice", $facreplaced->getNomUrl(1, '', 32)).'</span>';
4691 }
4692 if ($object->type == Facture::TYPE_CREDIT_NOTE && !empty($object->fk_facture_source)) {
4693 $facusing = new Facture($db);
4694 $facusing->fetch($object->fk_facture_source);
4695 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("CorrectInvoice", $facusing->getNomUrl(1, '', 32)).'</span>';
4696 }
4697
4698 $facidavoir = $object->getListIdAvoirFromInvoice();
4699 if (count($facidavoir) > 0) {
4700 print ' <span class="opacitymediumbycolor paddingleft">'.$langs->transnoentities("InvoiceHasAvoir");
4701 $i = 0;
4702 foreach ($facidavoir as $id) {
4703 if ($i == 0) {
4704 print ' ';
4705 } else {
4706 print ',';
4707 }
4708 $facavoir = new Facture($db);
4709 $facavoir->fetch($id);
4710 print $facavoir->getNomUrl(1, '', 32);
4711 }
4712 print '</span>';
4713 }
4714 if ($objectidnext > 0) {
4715 $facthatreplace = new Facture($db);
4716 $facthatreplace->fetch($objectidnext);
4717 print ' <span class="opacitymediumbycolor paddingleft">'.str_replace('{s1}', $facthatreplace->getNomUrl(1), $langs->transnoentities("ReplacedByInvoice", '{s1}')).'</span>';
4718 }
4719
4721 $discount = new DiscountAbsolute($db);
4722 $result = $discount->fetch(0, $object->id);
4723 if ($result > 0) {
4724 print ' <span class="opacitymediumbycolor paddingleft">';
4725 $s = $langs->trans("CreditNoteConvertedIntoDiscount", '{s1}', '{s2}');
4726 $s = str_replace('{s1}', $object->getLibType(0), $s);
4727 $s = str_replace('{s2}', $discount->getNomUrl(1, 'discount'), $s);
4728 print $s;
4729 print '</span><br>';
4730 }
4731 }
4732
4733 if ($object->fk_fac_rec_source > 0) {
4734 $tmptemplate = new FactureRec($db);
4735 $result = $tmptemplate->fetch($object->fk_fac_rec_source);
4736 if ($result > 0) {
4737 print ' <span class="opacitymediumbycolor paddingleft">';
4738 $s = $langs->transnoentities("GeneratedFromTemplate", '{s1}');
4739 $s = str_replace('{s1}', $tmptemplate->getNomUrl(1, '', 32), $s);
4740 print $s;
4741 print '</span>';
4742 }
4743 }
4744 print '</td></tr>';
4745
4746 // Relative and absolute discounts
4747 print '<!-- Discounts -->'."\n";
4748 print '<tr><td>'.$langs->trans('DiscountStillRemaining').'</td>';
4749 print '<td>';
4750 $thirdparty = $soc;
4751 $discount_type = 0;
4752 $backtopage = $_SERVER["PHP_SELF"].'?facid='.$object->id;
4753 include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php';
4754 print '</td></tr>';
4755
4756 // Date invoice
4757 print '<tr><td>';
4758 print '<table class="nobordernopadding centpercent"><tr><td>';
4759 print $langs->trans('DateInvoice');
4760 print '</td>';
4761 if ($action != 'editinvoicedate' && $object->status == $object::STATUS_DRAFT && $usercancreate && !getDolGlobalString(&