dolibarr 23.0.3
card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
5 * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2010-2023 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2010-2022 Philippe Grand <philippe.grand@atoo-net.com>
10 * Copyright (C) 2012-2023 Christophe Battarel <christophe.battarel@altairis.fr>
11 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
13 * Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
16 * Copyright (C) 2020 Nicolas ZABOURI <info@inovea-conseil.com>
17 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18 * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
19 * Copyright (C) 2023 William Mead <william.mead@manchenumerique.fr>
20 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
22 * Copyright (C) 2025 Benjamin Falière <benjamin@faliere.com>
23 * Copyright (C) 2025 Anthony Berton <anthony.berton@bb2a.fr>
24 *
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation; either version 3 of the License, or
28 * (at your option) any later version.
29 *
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
34 *
35 * You should have received a copy of the GNU General Public License
36 * along with this program. If not, see <https://www.gnu.org/licenses/>.
37 */
38
39
46// Load Dolibarr environment
47require '../../main.inc.php';
48require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
49require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
50require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php';
51require_once DOL_DOCUMENT_ROOT . '/core/class/html.formpropal.class.php';
52require_once DOL_DOCUMENT_ROOT . '/core/class/html.formmargin.class.php';
53require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
54require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
55require_once DOL_DOCUMENT_ROOT . '/core/modules/propale/modules_propale.php';
56require_once DOL_DOCUMENT_ROOT . '/core/lib/propal.lib.php';
57require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
58require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
59require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
60if (isModEnabled('project')) {
61 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
62 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
63}
64
65if (isModEnabled('variants')) {
66 require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
67}
68
78// Load translation files required by the page
79$langs->loadLangs(array('companies', 'propal', 'compta', 'bills', 'orders', 'products', 'sendings', 'other'));
80if (isModEnabled('incoterm')) {
81 $langs->load('incoterm');
82}
83if (isModEnabled('margin')) {
84 $langs->load('margins');
85}
86
87$error = 0;
88$outlangs = null;
89$array_options = array();
90
91$id = GETPOSTINT('id');
92$ref = GETPOST('ref', 'alpha');
93$socid = GETPOSTINT('socid');
94$action = GETPOST('action', 'aZ09');
95$cancel = GETPOST('cancel', 'alpha');
96$origin = GETPOST('origin', 'alpha');
97$originid = GETPOSTINT('originid');
98$renewal = GETPOST('renewal'); // for contract renewal
99$confirm = GETPOST('confirm', 'alpha');
100$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
101$lineid = GETPOSTINT('lineid');
102$contactid = GETPOSTINT('contactid');
103$projectid = GETPOSTINT('projectid');
104$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
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$object = new Propal($db);
112$extrafields = new ExtraFields($db);
113
114// fetch optionals attributes and labels
115$extrafields->fetch_name_optionals_label($object->table_element);
116
117// Load object
118if ($id > 0 || !empty($ref)) {
119 $ret = $object->fetch($id, $ref);
120 if ($ret > 0) {
121 $ret = $object->fetch_thirdparty();
122 if ($ret > 0 && isset($object->fk_project)) {
123 $ret = $object->fetchProject();
124 }
125 }
126 if ($ret <= 0) {
127 setEventMessages($object->error, $object->errors, 'errors');
128 $action = '';
129 }
130}
131
132// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
133$hookmanager->initHooks(array('propalcard', 'globalcard'));
134
135$usercanread = $user->hasRight("propal", "lire");
136$usercancreate = $user->hasRight("propal", "creer");
137$usercandelete = $user->hasRight("propal", "supprimer");
138
139$usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'close')));
140$usercanvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')));
141$usercansend = (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'send')));
142
143$usermustrespectpricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
144$usercancreateorder = ($user->hasRight('commande', 'creer') == 1);
145$usercancreateinvoice = ($user->hasRight('facture', 'creer') == 1);
146$usercancreatecontract = ($user->hasRight('contrat', 'creer') == 1);
147$usercancreateintervention = ($user->hasRight('ficheinter', 'creer') == 1);
148$usercancreatepurchaseorder = ($user->hasRight('fournisseur', 'commande', 'creer') || $user->hasRight('supplier_order', 'creer'));
149$usercanreopen = ((!getDolGlobalBool('MAIN_USE_ADVANCED_PERMS') && $usercanclose) || (getDolGlobalBool('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'reopen')));
150if (getDolGlobalBool('PROPAL_DISALLOW_REOPEN')) {
151 $usercanreopen = false;
152}
153
154$permissiontoadd = $usercancreate;
155$permissionnote = $usercancreate; // Used by the include of actions_setnotes.inc.php
156$permissiondellink = $usercancreate; // Used by the include of actions_dellink.inc.php
157$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdown.inc.php
158$permissiontoeditextra = $permissiontoadd;
159if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
160 // For action 'update_extras', is there a specific permission set for the attribute to update
161 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
162}
163
164$price_base_type = null;
165$shipping_method_id = null;
166$warehouse_id = -1;
167
168// Security check
169if (!empty($user->socid)) {
170 $socid = $user->socid;
171}
172restrictedArea($user, 'propal', $object->id);
173
174
175/*
176 * Actions
177 */
178
179$parameters = array('socid' => $socid);
180$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
181if ($reshook < 0) {
182 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
183}
184if (empty($reshook)) {
185 $backurlforlist = DOL_URL_ROOT . '/comm/propal/list.php';
186
187 if (empty($backtopage) || ($cancel && empty($id))) {
188 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
189 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
190 $backtopage = $backurlforlist;
191 } else {
192 $backtopage = DOL_URL_ROOT . '/comm/propal/card.php?id=' . ((!empty($id) && $id > 0) ? $id : '__ID__');
193 }
194 }
195 }
196
197 if ($cancel) {
198 if (!empty($backtopageforcancel)) {
199 header("Location: " . $backtopageforcancel);
200 exit;
201 } elseif (!empty($backtopage)) {
202 header("Location: " . $backtopage);
203 exit;
204 }
205 $action = '';
206 }
207
208 include DOL_DOCUMENT_ROOT . '/core/actions_setnotes.inc.php'; // Must be include, not includ_once
209
210 include DOL_DOCUMENT_ROOT . '/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
211
212 include DOL_DOCUMENT_ROOT . '/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
213
214 // Action clone object
215 if ($action == 'confirm_clone' && $confirm == 'yes' && $usercancreate) {
216 if (!($socid > 0)) {
217 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('IdThirdParty')), null, 'errors');
218 } else {
219 if ($object->id > 0) {
220 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY')) {
221 //Get difference between old and new delivery date and change lines according to difference
222 $date_delivery = dol_mktime(
223 12,
224 0,
225 0,
226 GETPOSTINT('date_deliverymonth'),
227 GETPOSTINT('date_deliveryday'),
228 GETPOSTINT('date_deliveryyear')
229 );
230 $date_delivery_old = $object->delivery_date;
231 if (!empty($date_delivery_old) && !empty($date_delivery)) {
232 //Attempt to get the date without possible hour rounding errors
233 $old_date_delivery = dol_mktime(
234 12,
235 0,
236 0,
237 (int) dol_print_date($date_delivery_old, '%m'),
238 (int) dol_print_date($date_delivery_old, '%d'),
239 (int) dol_print_date($date_delivery_old, '%Y')
240 );
241 //Calculate the difference and apply if necessary
242 $difference = $date_delivery - $old_date_delivery;
243 if ($difference != 0) {
244 $object->delivery_date = $date_delivery;
245 foreach ($object->lines as $line) {
246 if (isset($line->date_start)) {
247 $line->date_start += $difference;
248 }
249 if (isset($line->date_end)) {
250 $line->date_end += $difference;
251 }
252 }
253 }
254 }
255 }
256
257 $result = $object->createFromClone($user, $socid, (GETPOSTISSET('entity') ? GETPOSTINT('entity') : null), (GETPOST('update_prices') == 'on'), (GETPOST('update_desc') == 'on'));
258 if ($result > 0) {
259 $warningMsgLineList = array();
260 // check all product lines are to sell otherwise add a warning message for each product line is not to sell
261 foreach ($object->lines as $line) {
262 if (!is_object($line->product)) {
263 $line->fetch_product();
264 }
265 if (is_object($line->product) && $line->product->id > 0) {
266 if (empty($line->product->status)) {
267 $warningMsgLineList[$line->id] = $langs->trans('WarningLineProductNotToSell', $line->product->ref);
268 }
269 }
270 }
271 if (!empty($warningMsgLineList)) {
272 setEventMessages('', $warningMsgLineList, 'warnings');
273 }
274
275 header("Location: " . $_SERVER['PHP_SELF'] . '?id=' . $result);
276 exit();
277 } else {
278 if (count($object->errors) > 0) {
279 setEventMessages($object->error, $object->errors, 'errors');
280 }
281 $action = '';
282 }
283 }
284 }
285 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $usercanclose) {
286 // Cancel proposal
287 $result = $object->setCancel($user);
288 if ($result > 0) {
289 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
290 exit();
291 } else {
292 $langs->load("errors");
293 setEventMessages($object->error, $object->errors, 'errors');
294 }
295 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) {
296 // Delete proposal
297 $result = $object->delete($user);
298 if ($result > 0) {
299 header('Location: ' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1');
300 exit();
301 } else {
302 $langs->load("errors");
303 setEventMessages($object->error, $object->errors, 'errors');
304 }
305 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
306 // Remove line
307 $result = $object->deleteLine($lineid);
308 // reorder lines
309 if ($result > 0) {
310 $object->line_order(true);
311 } else {
312 $langs->load("errors");
313 setEventMessages($object->error, $object->errors, 'errors');
314 }
315
316 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
317 // Define output language
318 $outputlangs = $langs;
319 if (getDolGlobalInt('MAIN_MULTILANGS')) {
320 $outputlangs = new Translate("", $conf);
321 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
322 $outputlangs->setDefaultLang($newlang);
323 }
324 $ret = $object->fetch($id); // Reload to get new records
325 if ($ret > 0) {
326 $object->fetch_thirdparty();
327 }
328 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
329 }
330
331 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
332 exit();
333 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) {
334 // Delete line
335 $object->fetch($id);
336 $object->fetch_thirdparty();
337
338 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'));
339 if ($result > 0) {
340 // reorder lines
341 $object->line_order(true);
342 // Define output language
343 $outputlangs = $langs;
344 $newlang = '';
345 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
346 $newlang = GETPOST('lang_id');
347 }
348 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
349 $newlang = $object->thirdparty->default_lang;
350 }
351 if (!empty($newlang)) {
352 $outputlangs = new Translate("", $conf);
353 $outputlangs->setDefaultLang($newlang);
354 $outputlangs->load('products');
355 }
356 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
357 $ret = $object->fetch($id); // Reload to get new records
358 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
359 }
360 if ($result >= 0) {
361 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
362 exit();
363 }
364 } else {
365 setEventMessages($object->error, $object->errors, 'errors');
366 $action = '';
367 }
368 } elseif ($action == 'confirm_validate' && $confirm == 'yes' && $usercanvalidate) {
369 // Validation
370 $idwarehouse = GETPOSTINT('idwarehouse');
371 $result = $object->valid($user);
372 if ($result > 0 && getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
373 $result = $object->closeProposal($user, $object::STATUS_SIGNED);
374 }
375 if ($result >= 0) {
376 $ret = $object->fetch($id); // Reload to get new records
377 if ($ret > 0) {
378 $object->fetch_thirdparty();
379 }
380 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
381 $outputlangs = $langs;
382 $newlang = '';
383 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
384 $newlang = GETPOST('lang_id', 'aZ09');
385 }
386 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
387 $newlang = $object->thirdparty->default_lang;
388 }
389 if (!empty($newlang)) {
390 $outputlangs = new Translate("", $conf);
391 $outputlangs->setDefaultLang($newlang);
392 }
393 $model = $object->model_pdf;
394
395 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
396 }
397 } else {
398 $langs->load("errors");
399 if (count($object->errors) > 0) {
400 setEventMessages($object->error, $object->errors, 'errors');
401 } else {
402 setEventMessages($langs->trans($object->error), null, 'errors');
403 }
404 }
405 } elseif ($action == 'setdate' && $usercancreate) {
406 $datep = dol_mktime(12, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
407
408 if (empty($datep)) {
409 $error++;
410 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
411 }
412
413 if (!$error) {
414 $result = $object->set_date($user, $datep);
415 if ($result > 0 && !empty($object->duree_validite) && !empty($object->fin_validite)) {
416 $datev = $datep + ($object->duree_validite * 24 * 3600);
417 $result = $object->set_echeance($user, $datev, 1);
418 }
419 if ($result < 0) {
420 dol_print_error($db, $object->error);
421 } elseif (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
422 $outputlangs = $langs;
423 $newlang = '';
424 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
425 $newlang = GETPOST('lang_id', 'aZ09');
426 }
427 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
428 $newlang = $object->thirdparty->default_lang;
429 }
430 if (!empty($newlang)) {
431 $outputlangs = new Translate("", $conf);
432 $outputlangs->setDefaultLang($newlang);
433 }
434 $model = $object->model_pdf;
435 $ret = $object->fetch($id); // Reload to get new records
436 if ($ret > 0) {
437 $object->fetch_thirdparty();
438 }
439
440 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
441 }
442 }
443 } elseif ($action == 'setecheance' && $usercancreate) {
444 $result = $object->set_echeance($user, dol_mktime(12, 0, 0, GETPOSTINT('echmonth'), GETPOSTINT('echday'), GETPOSTINT('echyear')));
445 if ($result >= 0) {
446 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
447 $outputlangs = $langs;
448 $newlang = '';
449 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
450 $newlang = GETPOST('lang_id', 'aZ09');
451 }
452 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
453 $newlang = $object->thirdparty->default_lang;
454 }
455 if (!empty($newlang)) {
456 $outputlangs = new Translate("", $conf);
457 $outputlangs->setDefaultLang($newlang);
458 }
459 $model = $object->model_pdf;
460 $ret = $object->fetch($id); // Reload to get new records
461 if ($ret > 0) {
462 $object->fetch_thirdparty();
463 }
464
465 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
466 }
467 } else {
468 setEventMessages($object->error, $object->errors, 'errors');
469 }
470 } elseif ($action == 'setdate_livraison' && $usercancreate) {
471 $result = $object->setDeliveryDate($user, dol_mktime(12, 0, 0, GETPOSTINT('date_livraisonmonth'), GETPOSTINT('date_livraisonday'), GETPOSTINT('date_livraisonyear')));
472 if ($result < 0) {
473 dol_print_error($db, $object->error);
474 }
475 } elseif ($action == 'setref_client' && $usercancreate) {
476 // Positionne ref client
477 $result = $object->set_ref_client($user, GETPOST('ref_client'));
478 if ($result < 0) {
479 setEventMessages($object->error, $object->errors, 'errors');
480 }
481 } elseif ($action == 'set_incoterms' && isModEnabled('incoterm') && $usercancreate) {
482 // Set incoterm
483 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
484 } elseif ($action == 'settags' && isModEnabled('category') && $usercancreate) { // Set tags
485 $result = $object->setCategories(GETPOST('categories', 'array'));
486 } elseif ($action == 'add' && $usercancreate) {
487 // Create proposal
488 $object->socid = $socid;
489 $object->fetch_thirdparty();
490
491 $datep = dol_mktime(12, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
492 $date_delivery = dol_mktime(12, 0, 0, GETPOSTINT('date_livraisonmonth'), GETPOSTINT('date_livraisonday'), GETPOSTINT('date_livraisonyear'));
493 $duration = GETPOSTINT('duree_validite');
494
495 if (empty($datep)) {
496 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("DatePropal")), null, 'errors');
497 $action = 'create';
498 $error++;
499 }
500 if (empty($duration)) {
501 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ValidityDuration")), null, 'errors');
502 $action = 'create';
503 $error++;
504 }
505
506 if ($socid < 1) {
507 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Customer")), null, 'errors');
508
509 $action = 'create';
510 $error++;
511 }
512
513 if (!$error) {
514 $db->begin();
515
516 // If we select proposal to clone during creation (when option PROPAL_CLONE_ON_CREATE_PAGE is on)
517 if (GETPOST('createmode') == 'copy' && GETPOST('copie_propal')) {
518 if ($object->fetch(GETPOSTINT('copie_propal')) > 0) {
519 $object->ref = GETPOST('ref');
520 $object->datep = $datep;
521 $object->date = $datep;
522 $object->delivery_date = $date_delivery;
523 $object->availability_id = GETPOSTINT('availability_id');
524 $object->demand_reason_id = GETPOSTINT('demand_reason_id');
525 $object->fk_delivery_address = GETPOSTINT('fk_address');
526 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
527 $object->warehouse_id = GETPOSTINT('warehouse_id');
528 $object->duree_validite = $duration;
529 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
530 $object->deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
531 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
532 $object->fk_account = GETPOSTINT('fk_account');
533 $object->socid = GETPOSTINT('socid');
534 $object->contact_id = GETPOSTINT('contactid');
535 $object->fk_project = GETPOSTINT('projectid');
536 $object->model_pdf = GETPOST('model', 'alphanohtml');
537 $object->author = $user; // deprecated
538 $object->user_author_id = $user->id;
539 $object->note_private = GETPOST('note_private', 'restricthtml');
540 $object->note_public = GETPOST('note_public', 'restricthtml');
543 $object->fk_incoterms = GETPOSTINT('incoterm_id');
544 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
545 } else {
546 setEventMessages($langs->trans("ErrorFailedToCopyProposal", GETPOST('copie_propal')), null, 'errors');
547 }
548 } else {
549 $object->ref = GETPOST('ref');
550 $object->ref_customer = GETPOST('ref_client');
551 $object->ref_client = $object->ref_customer;
552 $object->datep = $datep;
553 $object->date = $datep;
554 $object->delivery_date = $date_delivery;
555 $object->availability_id = GETPOSTINT('availability_id');
556 $object->demand_reason_id = GETPOSTINT('demand_reason_id');
557 $object->fk_delivery_address = GETPOSTINT('fk_address');
558 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
559 $object->warehouse_id = GETPOSTINT('warehouse_id');
560 $object->duree_validite = price2num(GETPOST('duree_validite', 'alpha'));
561 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
562 $object->deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
563 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
564 $object->fk_account = GETPOSTINT('fk_account');
565 $object->contact_id = GETPOSTINT('contactid');
566 $object->fk_project = GETPOSTINT('projectid');
567 $object->model_pdf = GETPOST('model');
568 $object->author = $user; // deprecated
569 $object->user_author_id = $user->id;
570 $object->note_private = GETPOST('note_private', 'restricthtml');
571 $object->note_public = GETPOST('note_public', 'restricthtml');
572 $object->fk_incoterms = GETPOSTINT('incoterm_id');
573 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
574
575 $object->origin = GETPOST('origin');
576 $object->origin_id = GETPOSTINT('originid');
577
578 // Multicurrency
579 if (isModEnabled("multicurrency")) {
580 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
581 }
582
583 // Fill array 'array_options' with data from add form
584 $ret = $extrafields->setOptionalsFromPost(null, $object);
585 if ($ret < 0) {
586 $error++;
587 $action = 'create';
588 }
589 }
590
591 if (!$error) {
592 if ($origin && $originid) {
593 // Parse element/subelement (ex: project_task)
594 $element = $subelement = $origin;
595 $regs = array();
596 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
597 $element = $regs[1];
598 $subelement = $regs[2];
599 }
600
601 // For compatibility
602 if ($element == 'order') {
603 $element = $subelement = 'commande';
604 }
605 if ($element == 'propal') {
606 $element = 'comm/propal';
607 $subelement = 'propal';
608 }
609 if ($element == 'contract') {
610 $element = $subelement = 'contrat';
611 }
612 if ($element == 'inter') {
613 $element = $subelement = 'fichinter';
614 }
615 if ($element == 'shipping') {
616 $element = $subelement = 'expedition';
617 }
618
619 // If this is a renewal proposal for a contract, we might choose to systematically create a new contract,
620 if (($origin != 'contrat' || $renewal != 'true') && !getDolGlobalInt('CONTRACT_NEW_CONTRACT_ON_RENEWAL')) {
621 $object->origin = $origin;
622 $object->origin_id = $originid;
623
624 // Possibility to add external linked objects with hooks
625 $object->linked_objects[$object->origin] = $object->origin_id;
626 }
627 if (GETPOSTISARRAY('other_linked_objects')) {
628 $object->linked_objects = array_merge($object->linked_objects, GETPOST('other_linked_objects', 'array:int'));
629 }
630
631 $id = $object->create($user);
632 if ($id > 0) {
633 dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
634
635 $classname = ucfirst($subelement);
636 $srcobject = new $classname($db);
637 '@phan-var-force Commande|Propal|Contrat|Fichinter|Expedition $srcobject'; // Can be other class, but CommonObject is too generic
640 dol_syslog("Try to find source object origin=" . $object->origin . " originid=" . $object->origin_id . " to add lines");
641 $result = $srcobject->fetch($object->origin_id);
642
643 if ($result > 0) {
644 $lines = $srcobject->lines;
645 if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
646 $srcobject->fetch_lines();
647 $lines = $srcobject->lines;
648 }
649
650 $fk_parent_line = 0;
651 $num = count($lines);
652 for ($i = 0; $i < $num; $i++) {
653 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
654 $desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : '');
655
656 // Positive line
657 $product_type = ($lines[$i]->product_type ? (int) $lines[$i]->product_type : 0);
658
659 // Date start
660 $date_start = false;
661 if ($lines[$i]->date_debut_prevue) {
662 $date_start = $lines[$i]->date_debut_prevue;
663 }
664 if ($lines[$i]->date_debut_reel) {
665 $date_start = $lines[$i]->date_debut_reel;
666 }
667 if ($lines[$i]->date_start) {
668 $date_start = $lines[$i]->date_start;
669 }
670
671 // Date end
672 $date_end = false;
673 if ($lines[$i]->date_fin_prevue) {
674 $date_end = $lines[$i]->date_fin_prevue;
675 }
676 if ($lines[$i]->date_fin_reel) {
677 $date_end = $lines[$i]->date_fin_reel;
678 }
679 if ($lines[$i]->date_end) {
680 $date_end = $lines[$i]->date_end;
681 }
682
683 // For a contract renewal, we report duration, starting after the end of contract
684 if ($origin == 'contrat' && $renewal == 'true') {
685 if ($lines[$i]->date_start && $lines[$i]->date_end) {
686 $duration = $lines[$i]->date_end - $lines[$i]->date_start;
687 $date_start = $lines[$i]->date_end + 86400;
688 $date_end = $date_start + $duration;
689 }
690 }
691
692 // Reset fk_parent_line for no child products and special product
693 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
694 $fk_parent_line = 0;
695 }
696
697 // Extrafields
698 if (method_exists($lines[$i], 'fetch_optionals')) {
699 $lines[$i]->fetch_optionals();
700 $array_options = $lines[$i]->array_options;
701 }
702
703 $tva_tx = $lines[$i]->tva_tx;
704 if (!empty($lines[$i]->vat_src_code) && !preg_match('/\‍(/', $tva_tx)) {
705 $tva_tx .= ' (' . $lines[$i]->vat_src_code . ')';
706 }
707
708 $result = $object->addline($desc, $lines[$i]->subprice, $lines[$i]->qty, $tva_tx, $lines[$i]->localtax1_tx, $lines[$i]->localtax2_tx, $lines[$i]->fk_product, $lines[$i]->remise_percent, 'HT', 0, $lines[$i]->info_bits, $product_type, $lines[$i]->rang, $lines[$i]->special_code, $fk_parent_line, $lines[$i]->fk_fournprice, $lines[$i]->pa_ht, $label, $date_start, $date_end, $array_options, $lines[$i]->fk_unit);
709
710 if ($result > 0) {
711 $lineid = $result;
712 } else {
713 $lineid = 0;
714 $error++;
715 break;
716 }
717
718 // Defined the new fk_parent_line
719 if ($result > 0 && $lines[$i]->product_type == 9) {
720 $fk_parent_line = $result;
721 }
722 }
723
724 // Hooks
725 $parameters = array('objFrom' => $srcobject);
726 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
727 // modified by hook
728 if ($reshook < 0) {
729 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
730 $error++;
731 }
732 } else {
733 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
734 $error++;
735 }
736 } else {
737 setEventMessages($object->error, $object->errors, 'errors');
738 $error++;
739 }
740 } else {
741 // Standard creation
742 $id = $object->create($user);
743 }
744
745 if ($id > 0) {
746 // Insert default contacts if defined
747 if (GETPOST('contactid') > 0) {
748 $result = $object->add_contact(GETPOSTINT('contactid'), 'CUSTOMER', 'external');
749 if ($result < 0) {
750 $error++;
751 setEventMessages($langs->trans("ErrorFailedToAddContact"), null, 'errors');
752 }
753 }
754
755 if (getDolGlobalString('PROPOSAL_AUTO_ADD_AUTHOR_AS_CONTACT')) {
756 $result = $object->add_contact($user->id, 'SALESREPFOLL', 'internal');
757 if ($result < 0) {
758 $error++;
759 setEventMessages($langs->trans("ErrorFailedToAddUserAsContact"), null, 'errors');
760 }
761 }
762 if (isModEnabled('category')) {
763 $categories = GETPOST('categories', 'array');
764 if (method_exists($object, 'setCategories')) {
765 $object->setCategories($categories);
766 }
767 }
768
769 if (!$error) {
770 $db->commit();
771
772 // Define output language
773 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
774 $outputlangs = $langs;
775 $newlang = '';
776 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
777 $newlang = GETPOST('lang_id', 'aZ09');
778 }
779 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
780 $newlang = $object->thirdparty->default_lang;
781 }
782 if (!empty($newlang)) {
783 $outputlangs = new Translate("", $conf);
784 $outputlangs->setDefaultLang($newlang);
785 }
786 $model = $object->model_pdf;
787
788 $ret = $object->fetch($id); // Reload to get new records
789 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
790 if ($result < 0) {
791 dol_print_error($db, $object->error, $object->errors);
792 }
793 }
794
795 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
796 exit();
797 } else {
798 $db->rollback();
799 $action = 'create';
800 }
801 } else {
802 setEventMessages($object->error, $object->errors, 'errors');
803 $db->rollback();
804 $action = 'create';
805 }
806 }
807 }
808 } elseif ($action == 'classifybilled' && $usercanclose) {
809 // Classify billed
810 $db->begin();
811
812 $result = $object->classifyBilled($user, 0, '');
813 if ($result < 0) {
814 setEventMessages($object->error, $object->errors, 'errors');
815 $error++;
816 }
817
818 if (!$error) {
819 $db->commit();
820 } else {
821 $db->rollback();
822 }
823 } elseif ($action == 'confirm_closeas' && $usercanclose && !GETPOST('cancel', 'alpha')) {
824 // Close proposal
825 if (!(GETPOSTINT('statut') > 0)) {
826 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CloseAs")), null, 'errors');
827 $action = 'closeas';
828 } elseif (GETPOSTINT('statut') == $object::STATUS_SIGNED || GETPOSTINT('statut') == $object::STATUS_NOTSIGNED) {
829 $locationTarget = '';
830 // prevent browser refresh from closing proposal several times
831 if ($object->status == $object::STATUS_VALIDATED || (getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE') && $object->status == $object::STATUS_DRAFT)) {
832 $db->begin();
833
834 $oldstatus = $object->status;
835
836 $result = $object->closeProposal($user, GETPOSTINT('statut'), GETPOST('note_private'));
837
838 if ($result < 0) {
839 setEventMessages($object->error, $object->errors, 'errors');
840 $error++;
841 } else {
842 // Needed if object linked modified by trigger (because linked objects can't be fetched two times : linkedObjectsFullLoaded)
843 $locationTarget = DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id;
844 }
845
846 $deposit = null;
847
848 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
849
850 if (
851 !$error && GETPOSTINT('statut') == $object::STATUS_SIGNED && GETPOST('generate_deposit') == 'on'
852 && !empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')
853 ) {
854 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
855
856 $date = dol_mktime(0, 0, 0, GETPOSTINT('datefmonth'), GETPOSTINT('datefday'), GETPOSTINT('datefyear'));
857 $forceFields = array();
858
859 if (GETPOSTISSET('date_pointoftax')) {
860 $forceFields['date_pointoftax'] = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'));
861 }
862
863 $deposit = Facture::createDepositFromOrigin($object, $date, GETPOSTINT('cond_reglement_id'), $user, 0, GETPOSTINT('validate_generated_deposit') == 'on', $forceFields);
864
865 if ($deposit) {
866 setEventMessage('DepositGenerated');
867 $locationTarget = DOL_URL_ROOT . '/compta/facture/card.php?id=' . $deposit->id;
868 } else {
869 $error++;
870 setEventMessages("Failed to create down payment - " . $object->error, $object->errors, 'errors');
871 }
872 }
873
874 if (!$error) {
875 $db->commit();
876
877 if ($deposit && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
878 $ret = $deposit->fetch($deposit->id); // Reload to get new records
879 $outputlangs = $langs;
880
881 if (getDolGlobalInt('MAIN_MULTILANGS')) {
882 $outputlangs = new Translate('', $conf);
883 $outputlangs->setDefaultLang($deposit->thirdparty->default_lang);
884 $outputlangs->load('products');
885 }
886
887 $result = $deposit->generateDocument($deposit->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
888
889 if ($result < 0) {
890 setEventMessages($deposit->error, $deposit->errors, 'errors');
891 }
892 }
893
894 if ($locationTarget) {
895 header('Location: ' . $locationTarget);
896 exit;
897 }
898 } else {
899 $object->status = $oldstatus;
900 $object->statut = $oldstatus; // deprecated
901
902 $db->rollback();
903 $action = '';
904 }
905 }
906 }
907 } elseif ($action == 'confirm_reopen' && $usercanreopen && !GETPOST('cancel', 'alpha')) {
908 // Reopen proposal
909 // prevent browser refresh from reopening proposal several times
911 $db->begin();
912
913 $newstatus = (getDolGlobalInt('PROPAL_SKIP_ACCEPT_REFUSE') ? Propal::STATUS_DRAFT : Propal::STATUS_VALIDATED);
914 $result = $object->reopen($user, $newstatus);
915 if ($result < 0) {
916 setEventMessages($object->error, $object->errors, 'errors');
917 $error++;
918 } else {
919 $object->statut = $newstatus; // deprecated
920 $object->status = $newstatus;
921 }
922
923 if (!$error) {
924 $db->commit();
925 } else {
926 $db->rollback();
927 }
928 }
929 } elseif ($action == 'import_lines_from_object' && $usercancreate && $object->status == Propal::STATUS_DRAFT) {
930 // add lines from objectlinked
931 $fromElement = GETPOST('fromelement');
932 $fromElementid = GETPOST('fromelementid');
933 $importLines = GETPOST('line_checkbox');
934
935 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && ctype_alpha($fromElement) && !empty($fromElementid)) {
936 if ($fromElement == 'commande') {
937 dol_include_once('/' . $fromElement . '/class/' . $fromElement . '.class.php');
938 $lineClassName = 'OrderLine';
939 } elseif ($fromElement == 'propal') {
940 dol_include_once('/comm/' . $fromElement . '/class/' . $fromElement . '.class.php');
941 $lineClassName = 'PropaleLigne';
942 } elseif ($fromElement == 'facture') {
943 dol_include_once('/compta/' . $fromElement . '/class/' . $fromElement . '.class.php');
944 $lineClassName = 'FactureLigne';
945 } else {
946 $lineClassName = null;
947 }
948 $nextRang = count($object->lines) + 1;
949 $importCount = 0;
950 $error = 0;
951 foreach ($importLines as $lineId) {
952 $lineId = intval($lineId);
953 $originLine = new $lineClassName($db);
954 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
955 $originLine->fetch_optionals();
956 $desc = $originLine->desc;
957 $pu_ht = $originLine->subprice;
958 $qty = $originLine->qty;
959 $txtva = $originLine->tva_tx;
960 $txlocaltax1 = $originLine->localtax1_tx;
961 $txlocaltax2 = $originLine->localtax2_tx;
962 $fk_product = $originLine->fk_product;
963 $remise_percent = $originLine->remise_percent;
964 $date_start = $originLine->date_start;
965 $date_end = $originLine->date_end;
966 $fk_code_ventilation = 0;
967 $info_bits = $originLine->info_bits;
968 $fk_remise_except = $originLine->fk_remise_except;
969 $price_base_type = 'HT';
970 $pu_ttc = 0;
971 $type = $originLine->product_type;
972 $rang = $nextRang++;
973 $special_code = $originLine->special_code;
974 $origin = $originLine->element;
975 $origin_id = $originLine->id;
976 $fk_parent_line = 0;
977 $fk_fournprice = $originLine->fk_fournprice;
978 $pa_ht = $originLine->pa_ht;
979 $label = $originLine->label;
980 $array_options = $originLine->array_options;
981 $situation_percent = 100;
982 $fk_prev_id = '';
983 $fk_unit = $originLine->fk_unit;
984 $pu_ht_devise = $originLine->multicurrency_subprice;
985
986 $res = $object->addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $price_base_type, $pu_ttc, $info_bits, $type, $rang, $special_code, $fk_parent_line, $fk_fournprice, $pa_ht, $label, $date_start, $date_end, $array_options, $fk_unit, $origin, $origin_id, $pu_ht_devise, $fk_remise_except);
987
988 if ($res > 0) {
989 $importCount++;
990 } else {
991 $error++;
992 }
993 } else {
994 $error++;
995 }
996 }
997
998 if ($error) {
999 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
1000 }
1001 }
1002 } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) {
1003 $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0;
1004 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva);
1005 } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) {
1006 $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0;
1007 $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount);
1008 }
1009
1010 include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
1011
1012 // Actions to send emails
1013 $actiontypecode = 'AC_OTH_AUTO';
1014 $triggersendname = 'PROPAL_SENTBYMAIL';
1015 $autocopy = 'MAIN_MAIL_AUTOCOPY_PROPOSAL_TO';
1016 $trackid = 'pro' . $object->id;
1017 include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
1018
1019
1020 // Go back to draft
1021 if ($action == 'modif' && $usercancreate) {
1022 $result = $object->setDraft($user);
1023 if ($result < 0) {
1024 setEventMessages($object->error, $object->errors, 'errors');
1025 }
1026
1027 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1028 // Define output language
1029 $outputlangs = $langs;
1030 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1031 $outputlangs = new Translate("", $conf);
1032 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
1033 $outputlangs->setDefaultLang($newlang);
1034 }
1035 $ret = $object->fetch($id); // Reload to get new records
1036 if ($ret > 0) {
1037 $object->fetch_thirdparty();
1038 }
1039 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1040 }
1041 } elseif ($action == "setabsolutediscount" && $usercancreate) {
1042 if (GETPOSTINT("remise_id")) {
1043 if ($object->id > 0) {
1044 $result = $object->insert_discount(GETPOSTINT("remise_id"));
1045 if ($result < 0) {
1046 setEventMessages($object->error, $object->errors, 'errors');
1047 }
1048 }
1049 }
1050 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
1051 // Define date start and date end for all line
1052 $alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
1053 $alldate_end = dol_mktime(GETPOSTINT('alldate_endhour'), GETPOSTINT('alldate_endmin'), 0, GETPOSTINT('alldate_endmonth'), GETPOSTINT('alldate_endday'), GETPOSTINT('alldate_endyear'));
1054 foreach ($object->lines as $key => $line) {
1055 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1056 continue;
1057 }
1058 if ($line->product_type == 1) { // only service line
1059 $result = $object->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $alldate_start, $alldate_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1060 $object->lines[$key] = $object->line;
1061 }
1062 }
1063 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
1064 // Define a vat_rate for all lines
1065 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
1066 $vat_rate = str_replace('*', '', $vat_rate);
1067 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1068 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1069 foreach ($object->lines as $key => $line) {
1070 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1071 continue;
1072 }
1073 $result = $object->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1074 $object->lines[$key] = $object->line;
1075 }
1076 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
1077 // Define a discount for all lines
1078 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
1079 $remise_percent = str_replace('*', '', $remise_percent);
1080 foreach ($object->lines as $key => $line) {
1081 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1082 continue;
1083 }
1084 $tvatx = $line->tva_tx;
1085 if (!empty($line->vat_src_code)) {
1086 $tvatx .= ' (' . $line->vat_src_code . ')';
1087 }
1088 $result = $object->updateline($line->id, $line->subprice, $line->qty, (float) $remise_percent, $tvatx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1089 $object->lines[$key] = $object->line;
1090 }
1091 } elseif ($action == 'addline' && GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '' && $usercancreate) {
1092 // Define margin
1093 $margin_rate = (GETPOST('marginforalllines', 'alpha') ? GETPOST('marginforalllines', 'alpha') : 0);
1094 foreach ($object->lines as $key => $line) {
1095 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1096 continue;
1097 }
1098 $subprice = price2num($line->pa_ht * (1 + $margin_rate / 100), 'MU');
1099 $prod = new Product($db);
1100 $prod->fetch($line->fk_product);
1101 if ($prod->price_min > $subprice) {
1102 $price_subprice = price($subprice, 0, $outlangs, 1, -1, -1, 'auto');
1103 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1104 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1105 }
1106 // Manage $line->subprice and $line->multicurrency_subprice
1107 if ($line->subprice <> 0) {
1108 $multicurrency_subprice = (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1109 } else {
1110 $multicurrency_subprice = 0;
1111 }
1112 // Update DB
1113 $result = $object->updateline($line->id, (float) $subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $multicurrency_subprice);
1114 $object->lines[$key] = $object->line;
1115 // Update $object with new margin info
1116 // $line->price = $subprice;
1117 // $line->marge_tx = $margin_rate;
1118 // $line->marque_tx = $margin_rate * $line->pa_ht / (float) $subprice;
1119 // $line->total_ht = $line->qty * (float) $subprice;
1120 // $line->total_tva = $line->tva_tx * $line->qty * (float) $subprice;
1121 // $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice;
1122 // // Manage $line->subprice and $line->multicurrency_subprice
1123 // $line->multicurrency_total_ht = $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1124 // $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1125 // $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1126 // // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1127 // $line->subprice = (float) $subprice;
1128 // $line->multicurrency_subprice = $multicurrency_subprice;
1129 }
1130 } elseif ($action == 'confirm_addtitleline' && $usercancreate) {
1131 // Handling adding a new title line for subtotals module
1132
1133 $langs->load('subtotals');
1134
1135 $desc = GETPOST('subtotallinedesc', 'alphanohtml');
1136 $depth = GETPOSTINT('subtotallinelevel') ?? 1;
1137
1138 $subtotal_options = array();
1139
1140 foreach (Propal::$TITLE_OPTIONS as $option) {
1141 $value = GETPOST($option, 'alphanohtml');
1142 if ($value) {
1143 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1144 }
1145 }
1146
1147 // Insert line
1148 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1149
1150 if ($result >= 0) {
1151 if ($result == 0) {
1152 setEventMessages($object->error, $object->errors, 'warnings');
1153 }
1154 $ret = $object->fetch($object->id); // Reload to get new records
1155 $object->fetch_thirdparty();
1156
1157 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1158 // Define output language
1159 $outputlangs = $langs;
1160 $newlang = GETPOST('lang_id', 'alpha');
1161 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1162 $newlang = $object->thirdparty->default_lang;
1163 }
1164 if (!empty($newlang)) {
1165 $outputlangs = new Translate("", $conf);
1166 $outputlangs->setDefaultLang($newlang);
1167 }
1168
1169 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1170 }
1171 } else {
1172 setEventMessages($object->error, $object->errors, 'errors');
1173 }
1174 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1175 exit();
1176 } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) {
1177 // Handling adding a new subtotal line for subtotals module
1178
1179 $langs->load('subtotals');
1180
1181 $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml');
1182 foreach ($object->lines as $line) {
1183 if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) {
1184 $desc = $line->desc;
1185 $depth = -$line->qty;
1186 }
1187 }
1188
1189 $subtotal_options = array();
1190
1191 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1192 $value = GETPOST($option, 'alphanohtml');
1193 if ($value) {
1194 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1195 }
1196 }
1197
1198 // Insert line
1199 if (isset($desc) && isset($depth)) {
1200 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1201 } else {
1202 $object->errors[] = $langs->trans("CorrespondingTitleNotFound");
1203 }
1204
1205 if (isset($result) && $result >= 0) {
1206 $ret = $object->fetch($object->id); // Reload to get new records
1207 $object->fetch_thirdparty();
1208
1209 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1210 // Define output language
1211 $outputlangs = $langs;
1212 $newlang = GETPOST('lang_id', 'alpha');
1213 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1214 $newlang = $object->thirdparty->default_lang;
1215 }
1216 if (!empty($newlang)) {
1217 $outputlangs = new Translate("", $conf);
1218 $outputlangs->setDefaultLang($newlang);
1219 }
1220
1221 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1222 }
1223 } else {
1224 setEventMessages($object->error, $object->errors, 'errors');
1225 }
1226 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1227 exit();
1228 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && !GETPOST('markforalllines', 'alpha') && $usercancreate) {
1229 // Add line
1230 // Set if we used free entry or predefined product
1231 $predef = '';
1232 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
1233
1234 $price_ht = '';
1235 $price_ht_devise = '';
1236 $price_ttc = '';
1237 $price_ttc_devise = '';
1238
1239 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES'))
1240
1241 if (GETPOST('price_ht') !== '') {
1242 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
1243 }
1244 if (GETPOST('multicurrency_price_ht') !== '') {
1245 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
1246 }
1247 if (GETPOST('price_ttc') !== '') {
1248 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
1249 }
1250 if (GETPOST('multicurrency_price_ttc') !== '') {
1251 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
1252 }
1253
1254 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
1255 if ($prod_entry_mode == 'free') {
1256 $idprod = 0;
1257 } else {
1258 $idprod = GETPOSTINT('idprod');
1259
1260 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
1261 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
1262 $error++;
1263 }
1264 }
1265
1266 $tva_tx = GETPOST('tva_tx', 'alpha');
1267
1268 $qty = price2num(GETPOST('qty' . $predef, 'alpha'), 'MS', 2);
1269 $remise_percent = (GETPOSTISSET('remise_percent' . $predef) ? price2num(GETPOST('remise_percent' . $predef, 'alpha'), '', 2) : 0);
1270 if (empty($remise_percent)) {
1271 $remise_percent = 0;
1272 }
1273
1274 // Extrafields
1275 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1276 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
1277 // Unset extrafield
1278 if (is_array($extralabelsline)) {
1279 // Get extra fields
1280 foreach ($extralabelsline as $key => $value) {
1281 unset($_POST["options_" . $key]);
1282 }
1283 }
1284
1285 $price_to_test_sign = ($price_ht ? $price_ht : $price_ttc);
1286
1287 if ((empty($idprod) || $idprod < 0) && ($price_to_test_sign < 0) && ($qty < 0)) {
1288 $langs->load("errors");
1289 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1290 $error++;
1291 }
1292 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
1293 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1294 $error++;
1295 }
1296
1297 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && $price_ht === '' && $price_ht_devise === '' && $price_ttc === '' && $price_ttc_devise === '') { // Unit price can be 0 but not ''. Also price can be negative for proposal.
1298 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
1299 $error++;
1300 }
1301 if ($qty < 0 && !getDolGlobalString('PROPAL_ENABLE_NEGATIVE_QTY')) {
1302 setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1303 $error++;
1304 }
1305 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
1306 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")), null, 'errors');
1307 $error++;
1308 }
1309
1310 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
1311 if ($combinations = GETPOST('combinations', 'array:alphanohtml')) {
1312 //Check if there is a product with the given combination
1313 $prodcomb = new ProductCombination($db);
1314
1315 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
1316 $idprod = $res->fk_product_child;
1317 } else {
1318 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
1319 $error++;
1320 }
1321 }
1322 }
1323
1324 if (!$error && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
1325 $pu_ht = 0;
1326 $pu_ttc = 0;
1327 $pu_ht_devise = 0;
1328 $pu_ttc_devise = 0;
1329 $price_min = 0;
1330 $price_min_ttc = 0;
1331 $tva_npr = 0;
1332 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
1333
1334 $db->begin();
1335
1336 // $tva_tx can be 'x.x (XXX)'
1337
1338 // Ecrase $pu par celui du produit
1339 // Ecrase $desc par celui du produit
1340 // Replaces $fk_unit with the product unit
1341 if (!empty($idprod) && $idprod > 0) {
1342 $prod = new Product($db);
1343 $prod->fetch($idprod);
1344
1345 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
1346
1347 // Update if prices fields are defined
1348 /*$tva_tx = get_default_tva($mysoc, $object->thirdparty, $prod->id);
1349 $tva_npr = get_default_npr($mysoc, $object->thirdparty, $prod->id);
1350 if (empty($tva_tx)) {
1351 $tva_npr = 0;
1352 }*/
1353
1354 // Price unique per product
1355 $pu_ht = $prod->price;
1356 $pu_ttc = $prod->price_ttc;
1357 $price_min = $prod->price_min;
1358 $price_min_ttc = $prod->price_min_ttc;
1359 $price_base_type = $prod->price_base_type;
1360
1361 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1362 // If price per customer
1363 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1364 $prodcustprice = new ProductCustomerPrice($db);
1365 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1366
1367 // If a price per customer exist
1368 $pricebycustomerexist = false;
1369 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1370 if ($result >= 0) {
1371 // If there is some prices specific to the customer
1372 if (count($prodcustprice->lines) > 0) {
1373 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1374 foreach ($prodcustprice->lines as $k => $custprice_line) {
1375 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1376 $pricebycustomerexist = true;
1377 $pu_ht = price($custprice_line->price);
1378 $pu_ttc = price($custprice_line->price_ttc);
1379 $price_min = price($custprice_line->price_min);
1380 $price_min_ttc = price($custprice_line->price_min_ttc);
1381 $price_base_type = $custprice_line->price_base_type;
1382 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1383 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1384 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1385 }
1386 $tva_npr = $custprice_line->recuperableonly;
1387 if (empty($tva_tx)) {
1388 $tva_npr = 0;
1389 }*/
1390 break;
1391 }
1392 }
1393 }
1394 } else {
1395 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1396 }
1397
1398 if (!$pricebycustomerexist && !empty($object->thirdparty->price_level)) { // If price per segment
1399 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1400 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1401 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1402 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1403 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1404 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1405 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1406 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1407 }
1408 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1409 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1410 }
1411 }
1412 }
1413 } elseif (getDolGlobalString('PRODUIT_MULTIPRICES') && !empty($object->thirdparty->price_level)) { // If price per segment
1414 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1415 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1416 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1417 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1418 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1419 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1420 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1421 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1422 }
1423 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1424 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1425 }
1426 }
1427 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1428 // If price per customer
1429 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1430
1431 $prodcustprice = new ProductCustomerPrice($db);
1432
1433 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1434
1435 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1436 if ($result >= 0) {
1437 // If there is some prices specific to the customer
1438 if (count($prodcustprice->lines) > 0) {
1439 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1440 foreach ($prodcustprice->lines as $k => $custprice_line) {
1441 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1442 $pu_ht = price($custprice_line->price);
1443 $pu_ttc = price($custprice_line->price_ttc);
1444 $price_min = price($custprice_line->price_min);
1445 $price_min_ttc = price($custprice_line->price_min_ttc);
1446 $price_base_type = $custprice_line->price_base_type;
1447 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1448 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1449 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1450 }
1451 $tva_npr = $custprice_line->recuperableonly;
1452 if (empty($tva_tx)) {
1453 $tva_npr = 0;
1454 }*/
1455 break;
1456 }
1457 }
1458 }
1459 } else {
1460 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1461 }
1462 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY')) {
1463 // If price per quantity
1464 if ($prod->prices_by_qty[0]) { // yes, this product has some prices per quantity
1465 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1466 $pqp = GETPOSTINT('pbq');
1467
1468 // Search price into product_price_by_qty from $prod->id
1469 foreach ($prod->prices_by_qty_list[0] as $priceforthequantityarray) {
1470 if ($priceforthequantityarray['rowid'] != $pqp) {
1471 continue;
1472 }
1473 // We found the price
1474 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1475 $pu_ht = $priceforthequantityarray['unitprice'];
1476 } else {
1477 $pu_ttc = $priceforthequantityarray['unitprice'];
1478 }
1479 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1480 break;
1481 }
1482 }
1483 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
1484 // If price per quantity and customer
1485 if ($prod->prices_by_qty[$object->thirdparty->price_level]) { // yes, this product has some prices per quantity
1486 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1487 $pqp = GETPOSTINT('pbq');
1488
1489 // Search price into product_price_by_qty from $prod->id
1490 foreach ($prod->prices_by_qty_list[$object->thirdparty->price_level] as $priceforthequantityarray) {
1491 if ($priceforthequantityarray['rowid'] != $pqp) {
1492 continue;
1493 }
1494 // We found the price
1495 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1496 $pu_ht = $priceforthequantityarray['unitprice'];
1497 } else {
1498 $pu_ttc = $priceforthequantityarray['unitprice'];
1499 }
1500 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1501 break;
1502 }
1503 }
1504 }
1505
1506 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
1507 $tmpprodvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
1508
1509 // Set unit price to use
1510 if (!empty($price_ht) || (string) $price_ht === '0') {
1511 $pu_ht = (float) price2num($price_ht, 'MU');
1512 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1513 } elseif (!empty($price_ht_devise) || (string) $price_ht_devise === '0') {
1514 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1515 $pu_ttc_devise = (float) price2num((float) $pu_ht_devise * (1 + ((float) $tmpvat / 100)), 'MU');
1516 $pu_ht = '';
1517 $pu_ttc = '';
1518 } elseif (!empty($price_ttc) || (string) $price_ttc === '0') {
1519 $pu_ttc = (float) price2num($price_ttc, 'MU');
1520 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1521 } elseif (!empty($price_ttc_devise) || (string) $price_ttc_devise === '0') {
1522 $pu_ttc_devise = (float) price2num($price_ttc_devise, 'MU');
1523 $pu_ht_devise = (float) price2num((float) $pu_ttc_devise / (1 + ((float) $tmpvat / 100)), 'MU');
1524 $pu_ht = '';
1525 $pu_ttc = '';
1526 } elseif ($tmpvat != $tmpprodvat) {
1527 // Is this still used ?
1528 if ($price_base_type != 'HT') {
1529 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1530 } else {
1531 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1532 }
1533 }
1534
1535 $desc = '';
1536
1537 // Define output language
1538 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1539 $outputlangs = $langs;
1540 $newlang = '';
1541 if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
1542 $newlang = GETPOST('lang_id', 'aZ09');
1543 }
1544 if (empty($newlang)) {
1545 $newlang = $object->thirdparty->default_lang;
1546 }
1547 if (!empty($newlang)) {
1548 $outputlangs = new Translate("", $conf);
1549 $outputlangs->setDefaultLang($newlang);
1550 }
1551
1552 $desc = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
1553 } else {
1554 $desc = $prod->description;
1555 }
1556
1557 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
1558 // 'DoNotAutofillButAutoConcat'
1559 $desc = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
1560 } else {
1561 //'AutoFillFormFieldBeforeSubmit' or 'DoNotUseDescriptionOfProdut' => User has already done the modification they want
1562 $desc = $line_desc;
1563 }
1564
1565 // Add custom code and origin country into description
1566 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
1567 $tmptxt = '(';
1568 // Define output language
1569 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1570 $outputlangs = $langs;
1571 $newlang = '';
1572 if (/* empty($newlang) && */GETPOST('lang_id', 'alpha')) {
1573 $newlang = GETPOST('lang_id', 'alpha');
1574 }
1575 if (empty($newlang)) {
1576 $newlang = $object->thirdparty->default_lang;
1577 }
1578 if (!empty($newlang)) {
1579 $outputlangs = new Translate("", $conf);
1580 $outputlangs->setDefaultLang($newlang);
1581 $outputlangs->load('products');
1582 }
1583 if (!empty($prod->customcode)) {
1584 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1585 }
1586 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1587 $tmptxt .= ' - ';
1588 }
1589 if (!empty($prod->country_code)) {
1590 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $outputlangs, 0);
1591 }
1592 } else {
1593 if (!empty($prod->customcode)) {
1594 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1595 }
1596 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1597 $tmptxt .= ' - ';
1598 }
1599 if (!empty($prod->country_code)) {
1600 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $langs, 0);
1601 }
1602 }
1603 $tmptxt .= ')';
1604 $desc = dol_concatdesc($desc, $tmptxt);
1605 }
1606
1607 $type = $prod->type;
1608 $fk_unit = $prod->fk_unit;
1609 } else {
1610 $pu_ht = price2num($price_ht, 'MU');
1611 $pu_ttc = price2num($price_ttc, 'MU');
1612 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
1613 $tva_tx = str_replace('*', '', $tva_tx);
1614 if (empty($tva_tx)) {
1615 $tva_npr = 0;
1616 }
1617 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
1618 $desc = $line_desc;
1619 $type = GETPOST('type');
1620 $fk_unit = GETPOST('units', 'alpha');
1621 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1622 $pu_ttc_devise = price2num($price_ttc_devise, 'MU');
1623
1624 if ($pu_ttc && !$pu_ht) {
1625 $price_base_type = 'TTC';
1626 }
1627 }
1628
1629 $info_bits = 0;
1630 if ($tva_npr) {
1631 $info_bits |= 0x01;
1632 }
1633
1634 // Local Taxes
1635 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
1636 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
1637
1638 // Margin
1639 $fournprice = (int) (GETPOST('fournprice' . $predef) ? GETPOST('fournprice' . $predef) : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
1640 $buyingprice = price2num((GETPOST('buying_price' . $predef) != '' ? GETPOST('buying_price' . $predef) : ''), '', 2); // If buying_price is '0', we must keep this value
1641
1642 $date_start = dol_mktime(GETPOSTINT('date_start' . $predef . 'hour'), GETPOSTINT('date_start' . $predef . 'min'), GETPOSTINT('date_start' . $predef . 'sec'), GETPOSTINT('date_start' . $predef . 'month'), GETPOSTINT('date_start' . $predef . 'day'), GETPOSTINT('date_start' . $predef . 'year'));
1643 $date_end = dol_mktime(GETPOSTINT('date_end' . $predef . 'hour'), GETPOSTINT('date_end' . $predef . 'min'), GETPOSTINT('date_end' . $predef . 'sec'), GETPOSTINT('date_end' . $predef . 'month'), GETPOSTINT('date_end' . $predef . 'day'), GETPOSTINT('date_end' . $predef . 'year'));
1644
1645 // Prepare a price equivalent for minimum price check
1646 $pu_equivalent = $pu_ht;
1647 $pu_equivalent_ttc = $pu_ttc;
1648 $currency_tx = $object->multicurrency_tx;
1649
1650 // Check if we have a foreign currency
1651 // If so, we update the pu_equiv as the equivalent price in base currency
1652 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1653 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
1654 }
1655 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1656 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
1657 }
1658
1659 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
1660 /*
1661 if ($pu_equivalent) {
1662 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
1663 $pu_equivalent_ttc = ...
1664 } else {
1665 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
1666 $pu_equivalent_ht = ...
1667 }
1668 */
1669
1670 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
1671 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
1672
1673 //$desc = dol_htmlcleanlastbr($desc);
1674
1675 // Check price is not lower than minimum
1676 if ($usermustrespectpricemin) {
1677 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
1678 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1679 setEventMessages($mesg, null, 'errors');
1680 $error++;
1681 } 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') {
1682 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1683 setEventMessages($mesg, null, 'errors');
1684 $error++;
1685 }
1686 }
1687
1688 if (!$error) {
1689 // Insert line
1690 $result = $object->addline($desc, $pu_ht, (float) $qty, $tva_tx, $localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $price_base_type, $pu_ttc, $info_bits, $type, min($rank, count($object->lines) + 1), 0, GETPOSTINT('fk_parent_line'), (int) $fournprice, $buyingprice, $label, $date_start, $date_end, $array_options, $fk_unit, '', 0, (float) $pu_ht_devise);
1691
1692 if ($result > 0) {
1693 $db->commit();
1694
1695 $ret = $object->fetch($object->id); // Reload to get new records
1696 if ($ret > 0) {
1697 $object->fetch_thirdparty();
1698 }
1699
1700 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1701 // Define output language
1702 $outputlangs = $langs;
1703 $newlang = GETPOST('lang_id', 'alpha');
1704 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1705 $newlang = $object->thirdparty->default_lang;
1706 }
1707 if (!empty($newlang)) {
1708 $outputlangs = new Translate("", $conf);
1709 $outputlangs->setDefaultLang($newlang);
1710 }
1711 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1712 }
1713
1714 unset($_POST['prod_entry_mode']);
1715
1716 unset($_POST['qty']);
1717 unset($_POST['type']);
1718 unset($_POST['remise_percent']);
1719 unset($_POST['price_ht']);
1720 unset($_POST['multicurrency_price_ht']);
1721 unset($_POST['price_ttc']);
1722 unset($_POST['tva_tx']);
1723 unset($_POST['product_ref']);
1724 unset($_POST['product_label']);
1725 unset($_POST['product_desc']);
1726 unset($_POST['fournprice']);
1727 unset($_POST['buying_price']);
1728 unset($_POST['np_marginRate']);
1729 unset($_POST['np_markRate']);
1730 unset($_POST['dp_desc']);
1731 unset($_POST['idprod']);
1732 unset($_POST['units']);
1733
1734 unset($_POST['date_starthour']);
1735 unset($_POST['date_startmin']);
1736 unset($_POST['date_startsec']);
1737 unset($_POST['date_startday']);
1738 unset($_POST['date_startmonth']);
1739 unset($_POST['date_startyear']);
1740 unset($_POST['date_endhour']);
1741 unset($_POST['date_endmin']);
1742 unset($_POST['date_endsec']);
1743 unset($_POST['date_endday']);
1744 unset($_POST['date_endmonth']);
1745 unset($_POST['date_endyear']);
1746 } else {
1747 $db->rollback();
1748
1749 setEventMessages($object->error, $object->errors, 'errors');
1750 }
1751 }
1752 }
1753 } elseif ($action == 'addline' && $usercancreate && (
1754 (GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '') ||
1755 (GETPOST('submitforallmark', 'alpha') && GETPOST('markforalllines', 'alpha') !== ''))) {
1756 $outlangs = $langs;
1757 // Define margin
1758 $margin_rate = GETPOSTISSET('marginforalllines') ? GETPOST('marginforalllines', 'int') : '';
1759 $mark_rate = GETPOSTISSET('markforalllines') ? GETPOST('markforalllines', 'int') : '';
1760 foreach ($object->lines as &$line) if ($line->subprice > 0) {
1761 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1762 continue;
1763 }
1764 $subprice_multicurrency = $line->subprice;
1765 if (is_numeric($margin_rate) && $margin_rate > 0) {
1766 $line->subprice = (float) price2num((float) $line->pa_ht * (1 + (float) $margin_rate / 100), 'MU');
1767 } elseif (is_numeric($mark_rate) && $mark_rate > 0) {
1768 $line->subprice = (float) ($line->pa_ht / (1 - ((float) $mark_rate / 100)));
1769 } else {
1770 $line->subprice = (float) $line->pa_ht;
1771 }
1772
1773 $prod = new Product($db);
1774 $res = $prod->fetch($line->fk_product);
1775 if ($res > 0) {
1776 if ($prod->price_min > $line->subprice) {
1777 $price_subprice = price($line->subprice, 0, $outlangs, 1, -1, -1, 'auto');
1778 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1779 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1780 } else {
1781 setEventMessages($prod->error, $prod->errors, 'errors');
1782 }
1783 } else {
1784 setEventMessages($prod->error, $prod->errors, 'errors');
1785 }
1786
1787 // Manage $line->subprice and $line->multicurrency_subprice
1788 $multicurrency_subprice = (float) $line->subprice * $line->multicurrency_subprice / $subprice_multicurrency;
1789 // Update DB
1790 $result = $object->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $multicurrency_subprice);
1791 // Update $object with new margin info
1792 if ($result > 0) {
1793 if (is_numeric($margin_rate) && empty($mark_rate)) {
1794 $line->marge_tx = $margin_rate;
1795 } elseif (is_numeric($mark_rate) && empty($margin_rate)) {
1796 $line->marque_tx = $mark_rate;
1797 }
1798 $line->total_ht = $line->qty * (float) $line->subprice;
1799 $line->total_tva = $line->tva_tx * $line->qty * (float) $line->subprice;
1800 $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $line->subprice;
1801 // Manage $line->subprice and $line->multicurrency_subprice
1802 $line->multicurrency_total_ht = $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1803 $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1804 $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1805 // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1806 $line->multicurrency_subprice = $multicurrency_subprice;
1807 } else {
1808 setEventMessages($object->error, $object->errors, 'errors');
1809 }
1810 }
1811 } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1812 // Handling updating a title line for subtotals module
1813
1814 $langs->load('subtotals');
1815
1816 $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title");
1817 $depth = GETPOSTINT('line_depth') ?? 1;
1818
1819 $subtotal_options = array();
1820
1821 foreach (Propal::$TITLE_OPTIONS as $option) {
1822 $value = GETPOST($option, 'alphanohtml');
1823 if ($value) {
1824 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1825 }
1826 }
1827
1828 // Update line
1829 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1830
1831 if ($result >= 0) {
1832 if ($result == 0) {
1833 setEventMessages($object->error, $object->errors, 'warnings');
1834 }
1835 $ret = $object->fetch($object->id); // Reload to get new records
1836 $object->fetch_thirdparty();
1837
1838 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1839 // Define output language
1840 $outputlangs = $langs;
1841 $newlang = GETPOST('lang_id', 'alpha');
1842 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1843 $newlang = $object->thirdparty->default_lang;
1844 }
1845 if (!empty($newlang)) {
1846 $outputlangs = new Translate("", $conf);
1847 $outputlangs->setDefaultLang($newlang);
1848 }
1849
1850 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1851 }
1852 } else {
1853 setEventMessages($object->error, $object->errors, 'errors');
1854 }
1855 } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1856 // Handling updating a subtotal line for subtotals module
1857
1858 $langs->load('subtotals');
1859
1860 $desc = GETPOST('line_desc', 'alphanohtml');
1861 $depth = GETPOSTINT('line_depth');
1862
1863 $subtotal_options = array();
1864
1865 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1866 $value = GETPOST($option, 'alphanohtml');
1867 if ($value) {
1868 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1869 }
1870 }
1871
1872 // Update line
1873 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1874
1875 if ($result > 0) {
1876 $ret = $object->fetch($object->id); // Reload to get new records
1877 $object->fetch_thirdparty();
1878
1879 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1880 // Define output language
1881 $outputlangs = $langs;
1882 $newlang = GETPOST('lang_id', 'alpha');
1883 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1884 $newlang = $object->thirdparty->default_lang;
1885 }
1886 if (!empty($newlang)) {
1887 $outputlangs = new Translate("", $conf);
1888 $outputlangs->setDefaultLang($newlang);
1889 }
1890
1891 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1892 }
1893 } else {
1894 setEventMessages($object->error, $object->errors, 'errors');
1895 }
1896 } elseif ($action == 'updateline' && $usercancreate && GETPOST('save')) {
1897 // Update a line
1898
1899 // Clean parameters
1900 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml'));
1901
1902 // Define info_bits
1903 $info_bits = 0;
1904 if (preg_match('/\*/', GETPOST('tva_tx'))) {
1905 $info_bits |= 0x01;
1906 }
1907
1908 // Define vat_rate
1909 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx', 'alpha') : 0);
1910 $vat_rate = str_replace('*', '', $vat_rate);
1911 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1912 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1913 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
1914 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
1915
1916 // Add buying price
1917 $fournprice = (int) (GETPOST('fournprice') ? GETPOST('fournprice') : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
1918 $buyingprice = price2num((GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''), '', 2); // If buying_price is '0', we must keep this value
1919
1920 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
1921 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
1922
1923 $date_start = dol_mktime(GETPOSTINT('date_starthour'), GETPOSTINT('date_startmin'), GETPOSTINT('date_startsec'), GETPOSTINT('date_startmonth'), GETPOSTINT('date_startday'), GETPOSTINT('date_startyear'));
1924 $date_end = dol_mktime(GETPOSTINT('date_endhour'), GETPOSTINT('date_endmin'), GETPOSTINT('date_endsec'), GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'));
1925
1926 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1927 if (empty($remise_percent)) {
1928 $remise_percent = 0;
1929 }
1930
1931 // Prepare a price equivalent for minimum price check
1932 $pu_equivalent = $pu_ht;
1933 $pu_equivalent_ttc = $pu_ttc;
1934
1935 $currency_tx = $object->multicurrency_tx;
1936
1937 // Check if we have a foreign currency
1938 // If so, we update the pu_equiv as the equivalent price in base currency
1939 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1940 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
1941 }
1942 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1943 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
1944 }
1945
1946 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
1947 /*
1948 if ($pu_equivalent) {
1949 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
1950 $pu_equivalent_ttc = ...
1951 } else {
1952 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
1953 $pu_equivalent_ht = ...
1954 }
1955 */
1956
1957 // Extrafields
1958 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1959 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
1960 // Unset extrafield
1961 if (is_array($extralabelsline)) {
1962 // Get extra fields
1963 foreach ($extralabelsline as $key => $value) {
1964 unset($_POST["options_" . $key]);
1965 }
1966 }
1967
1968 // Define special_code for special lines
1969 $special_code = GETPOSTINT('special_code');
1970 if (!GETPOST('qty')) {
1971 $special_code = 3;
1972 }
1973
1974 $pu = $pu_ht;
1975 $price_base_type = 'HT';
1976 if (empty($pu) && !empty($pu_ttc)) {
1977 $pu = $pu_ttc;
1978 $price_base_type = 'TTC';
1979 }
1980
1981 // Check minimum price
1982 $productid = GETPOSTINT('productid');
1983 if (!empty($productid)) {
1984 $product = new Product($db);
1985 $res = $product->fetch($productid);
1986
1987 $type = $product->type;
1988
1989 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
1990
1991 $price_min = $product->price_min;
1992 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
1993 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
1994 }
1995 $price_min_ttc = $product->price_min_ttc;
1996 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
1997 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
1998 }
1999
2000 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
2001 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
2002
2003 // Check price is not lower than minimum
2004 if ($usermustrespectpricemin) {
2005 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2006 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2007 setEventMessages($mesg, null, 'errors');
2008 $error++;
2009 $action = 'editline';
2010 } 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') {
2011 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2012 setEventMessages($mesg, null, 'errors');
2013 $error++;
2014 $action = 'editline';
2015 }
2016 }
2017 } else {
2018 $type = GETPOST('type');
2019 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2020
2021 // Check parameters
2022 if (GETPOST('type') < 0) {
2023 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
2024 $error++;
2025 }
2026 }
2027
2028 if (!$error) {
2029 $db->begin();
2030
2031 if (!$user->hasRight('margins', 'creer')) {
2032 foreach ($object->lines as &$line) {
2033 if ($line->id == GETPOSTINT('lineid')) {
2034 $fournprice = $line->fk_fournprice;
2035 $buyingprice = (string) $line->pa_ht; // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
2036 break;
2037 }
2038 }
2039 }
2040
2041 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
2042
2043 $result = $object->updateline(GETPOSTINT('lineid'), (float) $pu, (float) $qty, $remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, $description, $price_base_type, $info_bits, $special_code, GETPOSTINT('fk_parent_line'), 0, (int) $fournprice, $buyingprice, $label, $type, $date_start, $date_end, $array_options, GETPOSTINT("units"), (float) $pu_ht_devise);
2044
2045 if ($result >= 0) {
2046 $db->commit();
2047
2048 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2049 // Define output language
2050 $outputlangs = $langs;
2051 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2052 $outputlangs = new Translate("", $conf);
2053 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
2054 $outputlangs->setDefaultLang($newlang);
2055 }
2056 $ret = $object->fetch($id); // Reload to get new records
2057 if ($ret > 0) {
2058 $object->fetch_thirdparty();
2059 }
2060 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2061 }
2062
2063 unset($_POST['qty']);
2064 unset($_POST['type']);
2065 unset($_POST['productid']);
2066 unset($_POST['remise_percent']);
2067 unset($_POST['price_ht']);
2068 unset($_POST['multicurrency_price_ht']);
2069 unset($_POST['price_ttc']);
2070 unset($_POST['tva_tx']);
2071 unset($_POST['product_ref']);
2072 unset($_POST['product_label']);
2073 unset($_POST['product_desc']);
2074 unset($_POST['fournprice']);
2075 unset($_POST['buying_price']);
2076
2077 unset($_POST['date_starthour']);
2078 unset($_POST['date_startmin']);
2079 unset($_POST['date_startsec']);
2080 unset($_POST['date_startday']);
2081 unset($_POST['date_startmonth']);
2082 unset($_POST['date_startyear']);
2083 unset($_POST['date_endhour']);
2084 unset($_POST['date_endmin']);
2085 unset($_POST['date_endsec']);
2086 unset($_POST['date_endday']);
2087 unset($_POST['date_endmonth']);
2088 unset($_POST['date_endyear']);
2089 } else {
2090 $db->rollback();
2091
2092 setEventMessages($object->error, $object->errors, 'errors');
2093 }
2094 }
2095 } elseif ($action == 'updateline' && $usercancreate && GETPOST('cancel', 'alpha')) {
2096 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To re-display card in edit mode
2097 exit();
2098 } elseif ($action == 'classin' && $usercancreate) {
2099 // Set project
2100 $object->setProject(GETPOSTINT('projectid'));
2101 } elseif ($action == 'setavailability' && $usercancreate) {
2102 // Delivery time
2103 $result = $object->set_availability($user, GETPOSTINT('availability_id'));
2104 } elseif ($action == 'setdemandreason' && $usercancreate) {
2105 // Origin of the commercial proposal
2106 $result = $object->set_demand_reason($user, GETPOSTINT('demand_reason_id'));
2107 } elseif ($action == 'setconditions' && $usercancreate) {
2108 // Terms of payment
2109 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'), GETPOSTFLOAT('cond_reglement_id_deposit_percent'));
2110 } elseif ($action == 'setmode' && $usercancreate) {
2111 // Payment choice
2112 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
2113 } elseif ($action == 'setmulticurrencycode' && $usercancreate) {
2114 // Multicurrency Code
2115 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
2116 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) {
2117 // Multicurrency rate
2118 $result = $object->setMulticurrencyRate(GETPOSTFLOAT('multicurrency_tx'), GETPOSTINT('calculation_mode'));
2119 } elseif ($action == 'setbankaccount' && $usercancreate) {
2120 // bank account
2121 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
2122 } elseif ($action == 'setshippingmethod' && $usercancreate) {
2123 // shipping method
2124 $result = $object->setShippingMethod(GETPOSTINT('shipping_method_id'));
2125 } elseif ($action == 'setwarehouse' && $usercancreate) {
2126 // warehouse
2127 $result = $object->setWarehouse(GETPOSTINT('warehouse_id'));
2128 } elseif ($action == 'update_extras' && $permissiontoeditextra) {
2129 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
2130
2131 $attribute_name = GETPOST('attribute', 'aZ09');
2132
2133 // Fill array 'array_options' with data from update form
2134 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
2135 if ($ret < 0) {
2136 $error++;
2137 }
2138 if (!$error) {
2139 $result = $object->updateExtraField($attribute_name, 'PROPAL_MODIFY');
2140 if ($result < 0) {
2141 setEventMessages($object->error, $object->errors, 'errors');
2142 $error++;
2143 }
2144 }
2145 if ($error) {
2146 $action = 'edit_extras';
2147 }
2148 }
2149
2150 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
2151 if ($action == 'addcontact' && $usercancreate) {
2152 if ($object->id > 0) {
2153 $contactid = (GETPOST('userid') ? GETPOSTINT('userid') : GETPOSTINT('contactid'));
2154 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
2155 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
2156 }
2157
2158 if ($result >= 0) {
2159 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2160 exit();
2161 } else {
2162 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2163 $langs->load("errors");
2164 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
2165 } else {
2166 setEventMessages($object->error, $object->errors, 'errors');
2167 }
2168 }
2169 } elseif ($action == 'swapstatut' && $usercancreate) {
2170 // Toggle the status of a contact
2171 if ($object->fetch($id) > 0) {
2172 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
2173 } else {
2174 dol_print_error($db);
2175 }
2176 } elseif ($action == 'deletecontact' && $usercancreate) {
2177 // Delete a contact
2178 $object->fetch($id);
2179 $result = $object->delete_contact($lineid);
2180
2181 if ($result >= 0) {
2182 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2183 exit();
2184 } else {
2185 dol_print_error($db);
2186 }
2187 }
2188 }
2189
2190 // Actions to build doc
2191 $upload_dir = !empty($conf->propal->multidir_output[$object->entity ?? $conf->entity]) ? $conf->propal->multidir_output[$object->entity ?? $conf->entity] : $conf->propal->dir_output;
2192 $permissiontoadd = $usercancreate;
2193 include DOL_DOCUMENT_ROOT . '/core/actions_builddoc.inc.php';
2194}
2195
2196
2197/*
2198 * View
2199 */
2200
2201$form = new Form($db);
2202$formfile = new FormFile($db);
2203$formpropal = new FormPropal($db);
2204$formmargin = new FormMargin($db);
2205$formproject = null;
2206if (isModEnabled('project')) {
2207 $formproject = new FormProjets($db);
2208}
2209
2210$title = $object->ref . " - " . $langs->trans('Card');
2211if ($action == 'create') {
2212 $title = $langs->trans("NewPropal");
2213}
2214$help_url = 'EN:Commercial_Proposals|FR:Proposition_commerciale|ES:Presupuestos|DE:Modul_Angebote';
2215
2216llxHeader('', $title, $help_url);
2217
2218$now = dol_now();
2219
2220// Add new proposal
2221if ($action == 'create') {
2222 $currency_code = getDolCurrency();
2223
2224 print load_fiche_titre($langs->trans("NewProp"), '', 'propal');
2225
2226 $soc = new Societe($db);
2227 if ($socid > 0) {
2228 $res = $soc->fetch($socid);
2229 }
2230
2231 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2232 $deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
2233 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2234 $fk_account = GETPOSTINT('fk_account');
2235 $datepropal = (getDolGlobalString('MAIN_DO_NOT_AUTOFILL_DATE_PROPOSAL') ? -1 : ''); // By default '' so we will autofill date. -1 means keep empty.
2236
2237 // Load objectsrc
2238 $objectsrc = null;
2239 if (!empty($origin) && !empty($originid)) {
2240 // Parse element/subelement (ex: project_task)
2241 $element = $subelement = $origin;
2242 $regs = array();
2243 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
2244 $element = $regs[1];
2245 $subelement = $regs[2];
2246 }
2247
2248 if ($element == 'project') {
2249 $projectid = $originid;
2250
2251 // Fetch project and thirdparty
2252 $project = new Project($db);
2253 $project->fetch($projectid);
2254 if ($project->socid > 0) {
2255 $soc = new Societe($db);
2256 $soc->fetch($project->socid);
2257 }
2258 } else {
2259 // For compatibility
2260 if ($element == 'order' || $element == 'commande') {
2261 $element = $subelement = 'commande';
2262 }
2263 if ($element == 'propal') {
2264 $element = 'comm/propal';
2265 $subelement = 'propal';
2266 }
2267 if ($element == 'contract') {
2268 $element = $subelement = 'contrat';
2269 }
2270 if ($element == 'shipping') {
2271 $element = $subelement = 'expedition';
2272 }
2273
2274 dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
2275
2276 $classname = ucfirst($subelement);
2277 $objectsrc = new $classname($db);
2278 '@phan-var-force Commande|Propal|Contrat|Expedition $objectsrc'; // Can be other class, but CommonObject is too generic
2280 $objectsrc->fetch($originid);
2281 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
2282 $objectsrc->fetch_lines();
2283 }
2284 $objectsrc->fetch_thirdparty();
2285
2286 $projectid = (int) $objectsrc->fk_project;
2287 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : '');
2288
2289 $soc = $objectsrc->thirdparty;
2290
2291 $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 0));
2292 $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
2293 $warehouse_id = (!empty($objectsrc->warehouse_id) ? $objectsrc->warehouse_id : (!empty($soc->warehouse_id) ? $soc->warehouse_id : -1));
2294
2295 // Replicate extrafields
2296 $objectsrc->fetch_optionals();
2297 $object->array_options = $objectsrc->array_options;
2298
2299 if (isModEnabled("multicurrency")) {
2300 if (!empty($objectsrc->multicurrency_code)) {
2301 $currency_code = $objectsrc->multicurrency_code;
2302 }
2303 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
2304 $currency_tx = $objectsrc->multicurrency_tx;
2305 }
2306 }
2307 }
2308 }
2309
2310 // Load default values from thirdparty
2311 if (!empty($soc)) {
2312 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
2313 $deposit_percent = empty($soc->deposit_percent) ? $deposit_percent : $soc->deposit_percent;
2314 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
2315 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
2316 $shipping_method_id = $soc->shipping_method_id;
2317 $warehouse_id = !empty($soc->fk_warehouse) ? $soc->fk_warehouse : $warehouse_id;
2318 $remise_percent = $soc->remise_percent;
2319
2320 if (isModEnabled("multicurrency") && !empty($soc->multicurrency_code)) {
2321 $currency_code = $soc->multicurrency_code;
2322 }
2323 }
2324
2325 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
2326 if (!GETPOST('changecompany')) {
2327 if (GETPOSTISSET('cond_reglement_id')) {
2328 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2329 }
2330 if (GETPOSTISSET('deposit_percent')) {
2331 $deposit_percent = price2num(GETPOST('deposit_percent', 'alpha'));
2332 }
2333 if (GETPOSTISSET('mode_reglement_id')) {
2334 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2335 }
2336 if (GETPOSTISSET('fk_account')) {
2337 $fk_account = GETPOSTINT('fk_account');
2338 }
2339 }
2340 // Warehouse default if null
2341 if ($soc->fk_warehouse > 0) {
2342 $warehouse_id = $soc->fk_warehouse;
2343 }
2344 if (isModEnabled('stock') && $warehouse_id < 0 && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2345 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
2346 $warehouse_id = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
2347 }
2348 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && !empty($user->fk_warehouse)) {
2349 $warehouse_id = $user->fk_warehouse;
2350 }
2351 }
2352
2353 print '<form name="addprop" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
2354 print '<input type="hidden" name="token" value="' . newToken() . '">';
2355 print '<input type="hidden" name="action" value="add">';
2356 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
2357 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
2358 if ($origin != 'project' && $originid) {
2359 print '<input type="hidden" name="origin" value="' . $origin . '">';
2360 print '<input type="hidden" name="originid" value="' . $originid . '">';
2361 if ($origin == 'contrat' && !empty($renewal)) {
2362 print '<input type="hidden" name="renewal" value="' . $renewal . '">';
2363 }
2364 } elseif ($origin == 'project' && !empty($projectid)) {
2365 print '<input type="hidden" name="projectid" value="' . $projectid . '">';
2366 }
2367
2368 print dol_get_fiche_head();
2369
2370 // Call Hook tabContentCreateProposal
2371 $parameters = array();
2372 // Note that $action and $object may be modified by hook
2373 $reshook = $hookmanager->executeHooks('tabContentCreateProposal', $parameters, $object, $action);
2374 if (empty($reshook)) {
2375 print '<table class="border centpercent">';
2376
2377 // Reference
2378 print '<tr class="field_ref"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Ref') . '</td><td class="valuefieldcreate">' . $langs->trans("Draft") . '</td></tr>';
2379
2380 // Ref customer
2381 print '<tr class="field_ref_client"><td class="titlefieldcreate">' . $langs->trans('RefCustomer') . '</td><td class="valuefieldcreate">';
2382 print '<input type="text" name="ref_client" value="' . (!empty($ref_client) ? $ref_client : GETPOST('ref_client')) . '"></td>';
2383 print '</tr>';
2384
2385 // Third party
2386 print '<tr class="field_socid">';
2387
2388 print '<td class="titlefieldcreate fieldrequired">' . $langs->trans('Customer') . '</td>';
2389 $shipping_method_id = 0;
2390 $warehouse_id = 0;
2391
2392 if ($socid > 0) {
2393 print '<td class="valuefieldcreate">';
2394 print $soc->getNomUrl(1, 'customer');
2395 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
2396 print '</td>';
2397 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2398 $shipping_method_id = $soc->shipping_method_id;
2399 }
2400 } else {
2401 print '<td class="valuefieldcreate">';
2402 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
2403 print img_picto('', 'company', 'class="pictofixedwidth"') . $form->select_company('', 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 maxwidth500 widthcentpercentminusxx');
2404 // reload page to retrieve customer information
2405 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
2406 print '<script>
2407 $(document).ready(function() {
2408 $("#socid").change(function() {
2409 console.log("We have changed the company - Reload page");
2410 var socid = $(this).val();
2411 // reload page
2412 $("input[name=action]").val("create");
2413 $("input[name=changecompany]").val("1");
2414 $("form[name=addprop]").submit();
2415 });
2416 });
2417 </script>';
2418 }
2419 print ' <a href="' . DOL_URL_ROOT . '/societe/card.php?action=create&prospect=3&fournisseur=0&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create') . '"><span class="fa fa-plus-circle valignmiddle paddingleft" title="' . $langs->trans("AddThirdParty") . '"></span></a>';
2420 print '</td>';
2421 }
2422 print '</tr>' . "\n";
2423
2424 if ($socid > 0) {
2425 // Contacts (ask contact only if thirdparty already defined).
2426 print '<tr class="field_contactid"><td class="titlefieldcreate">' . $langs->trans("DefaultContact") . '</td><td class="valuefieldcreate">';
2427 print img_picto('', 'contact', 'class="pictofixedwidth"');
2428 //print $form->selectcontacts($soc->id, $contactid, 'contactid', 1, '', '', 0, 'minwidth300 widthcentpercentminusx');
2429 print $form->select_contact($soc->id, $contactid, 'contactid', 1, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
2430 print '</td></tr>';
2431
2432 // Third party discounts info line
2433 print '<tr class="field_discount_info"><td class="titlefieldcreate">' . $langs->trans('Discounts') . '</td><td class="valuefieldcreate">';
2434
2435 $absolute_discount = $soc->getAvailableDiscounts();
2436
2437 $thirdparty = $soc;
2438 $discount_type = 0;
2439 $backtopage = $_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . urlencode((string) (GETPOST('origin'))) . '&originid=' . urlencode((string) (GETPOSTINT('originid')));
2440 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
2441 print '</td></tr>';
2442 }
2443
2444 $newdatepropal = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
2445 // Date
2446 print '<tr class="field_addprop"><td class="titlefieldcreate fieldrequired">' . $langs->trans('DatePropal') . '</td><td class="valuefieldcreate">';
2447 print img_picto('', 'action', 'class="pictofixedwidth"');
2448 print $form->selectDate($newdatepropal ? $newdatepropal : $datepropal, '', 0, 0, 0, "addprop", 1, 1);
2449 print '</td></tr>';
2450
2451 // Validaty duration
2452 print '<tr class="field_duree_validitee"><td class="titlefieldcreate fieldrequired">' . $langs->trans("ValidityDuration") . '</td><td class="valuefieldcreate">' . img_picto('', 'clock', 'class="pictofixedwidth"') . '<input name="duree_validite" class="width50" value="' . (GETPOSTISSET('duree_validite') ? GETPOST('duree_validite', 'alphanohtml') : getDolGlobalString('PROPALE_VALIDITY_DURATION')) . '"> ' . $langs->trans("days") . '</td></tr>';
2453
2454 // Terms of payment
2455 print '<tr class="field_cond_reglement_id"><td class="nowrap">' . $langs->trans('PaymentConditionsShort') . '</td><td>';
2456 print img_picto('', 'payment', 'class="pictofixedwidth"');
2457 // at last resort we take the payment term id which may be filled by default values set (if not getpostisset)
2458 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', 1, 1, 0, '', $deposit_percent);
2459 print '</td></tr>';
2460
2461 // Mode of payment
2462 print '<tr class="field_mode_reglement_id"><td class="titlefieldcreate">' . $langs->trans('PaymentMode') . '</td><td class="valuefieldcreate">';
2463 print img_picto('', 'bank', 'class="pictofixedwidth"');
2464 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth200 widthcentpercentminusx', 1);
2465 print '</td></tr>';
2466
2467 // Bank Account
2468 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
2469 print '<tr class="field_fk_account"><td class="titlefieldcreate">' . $langs->trans('BankAccount') . '</td><td class="valuefieldcreate">';
2470 print img_picto('', 'bank_account', 'class="pictofixedwidth"') . $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth200 widthcentpercentminusx', 1);
2471 print '</td></tr>';
2472 }
2473
2474 // Source / Channel - What trigger creation
2475 print '<tr class="field_demand_reason_id"><td class="titlefieldcreate">' . $langs->trans('Source') . '</td><td class="valuefieldcreate">';
2476 print img_picto('', 'question', 'class="pictofixedwidth"');
2477 $form->selectInputReason((GETPOSTISSET('demand_reason_id') ? GETPOSTINT('demand_reason_id') : ''), 'demand_reason_id', "SRC_PROP", 1, 'maxwidth200 widthcentpercentminusx');
2478 print '</td></tr>';
2479
2480 // Shipping Method
2481 if (isModEnabled("shipping")) {
2482 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2483 $shipping_method_id = $soc->shipping_method_id;
2484 }
2485 print '<tr class="field_shipping_method_id"><td class="titlefieldcreate">' . $langs->trans('SendingMethod') . '</td><td class="valuefieldcreate">';
2486 print img_picto('', 'dolly', 'class="pictofixedwidth"');
2487 $form->selectShippingMethod((string) (GETPOSTISSET('shipping_method_id') ? GETPOSTINT('shipping_method_id') : $shipping_method_id), 'shipping_method_id', '', 1, '', 0, 'maxwidth200 widthcentpercentminusx');
2488 print '</td></tr>';
2489 }
2490
2491 $formproduct = null;
2492 // Warehouse
2493 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2494 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
2495 $formproduct = new FormProduct($db);
2496 print '<tr class="field_warehouse_id"><td class="titlefieldcreate">' . $langs->trans('Warehouse') . '</td><td class="valuefieldcreate">';
2497 print img_picto('', 'stock', 'class="pictofixedwidth"') . $formproduct->selectWarehouses((int) $warehouse_id, 'warehouse_id', '', 1, 0, 0, '', 0, 0, array(), 'maxwidth500 widthcentpercentminusxx');
2498 print '</td></tr>';
2499 }
2500
2501 // Delivery delay
2502 print '<tr class="field_availability_id"><td class="titlefieldcreate">' . $langs->trans('AvailabilityPeriod');
2503 if (isModEnabled('order')) {
2504 print ' (' . $langs->trans('AfterOrder') . ')';
2505 }
2506 print '</td><td class="valuefieldcreate">';
2507 print img_picto('', 'clock', 'class="pictofixedwidth"');
2508 $form->selectAvailabilityDelay((GETPOSTISSET('availability_id') ? GETPOSTINT('availability_id') : ''), 'availability_id', '', 1, 'maxwidth200 widthcentpercentminusx');
2509 print '</td></tr>';
2510
2511 // Delivery date (or manufacturing)
2512 print '<tr class="field_date_livraison"><td class="titlefieldcreate">' . $langs->trans("DeliveryDate") . '</td>';
2513 print '<td class="valuefieldcreate">';
2514 print img_picto('', 'action', 'class="pictofixedwidth"');
2515 if (is_numeric(getDolGlobalString('DATE_LIVRAISON_WEEK_DELAY'))) { // If value set to 0 or a num, not empty
2516 $tmpdte = time() + (7 * getDolGlobalInt('DATE_LIVRAISON_WEEK_DELAY') * 24 * 60 * 60);
2517 $syear = date("Y", $tmpdte);
2518 $smonth = date("m", $tmpdte);
2519 $sday = date("d", $tmpdte);
2520 print $form->selectDate($syear . "-" . $smonth . "-" . $sday, 'date_livraison', 0, 0, 0, "addprop");
2521 } else {
2522 $tmp_date_delivery = GETPOST('date_delivery') ?: -1;
2523 print $form->selectDate($tmp_date_delivery, 'date_livraison', 0, 0, 0, "addprop", 1, 1);
2524 }
2525 print '</td></tr>';
2526
2527 // Project
2528 if (isModEnabled('project') && is_object($formproject)) {
2529 $langs->load("projects");
2530 print '<tr class="field_projectid">';
2531 print '<td class="titlefieldcreate">'.$langs->trans("Project").'</td><td class="valuefieldcreate">';
2532 print img_picto('', 'project', 'class="pictofixedwidth"').$formproject->select_projects((($soc->id > 0 && !getDolGlobalString('PROJECT_CAN_ALWAYS_LINK_TO_ALL_CUSTOMERS')) ? $soc->id : -1), (string) $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500 widthcentpercentminusxx');
2533 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).'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddProject").'"></span></a>';
2534 print '</td>';
2535 print '</tr>';
2536 }
2537
2538 // Incoterms
2539 if (isModEnabled('incoterm')) {
2540 print '<tr class="field_incoterm_id">';
2541 print '<td class="titlefieldcreate"><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $soc->label_incoterms, 1) . '</label></td>';
2542 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2543 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
2544 print $form->select_incoterms((!empty($soc->fk_incoterms) ? $soc->fk_incoterms : ''), (!empty($soc->location_incoterms) ? $soc->location_incoterms : ''));
2545 print '</td></tr>';
2546 }
2547 if (isModEnabled('category')) {
2548 // Categories
2549 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
2550 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
2551 print "</td></tr>";
2552 }
2553 // Template to use by default
2554 print '<tr class="field_model">';
2555 print '<td class="titlefieldcreate">' . $langs->trans("DefaultModel") . '</td>';
2556 print '<td class="valuefieldcreate">';
2557 print img_picto('', 'pdf', 'class="pictofixedwidth"');
2559 $preselected = getDolGlobalString('PROPALE_ADDON_PDF_ODT_DEFAULT', getDolGlobalString("PROPALE_ADDON_PDF"));
2560 print $form->selectarray('model', $liste, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
2561 print "</td></tr>";
2562
2563 // Multicurrency
2564 if (isModEnabled("multicurrency")) {
2565 print '<tr class="field_currency">';
2566 print '<td class="titlefieldcreate">' . $form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0) . '</td>';
2567 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2568 print img_picto('', 'currency', 'class="pictofixedwidth"') . $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
2569 print '</td></tr>';
2570 }
2571
2572 // Public note
2573 print '<tr class="field_note_public">';
2574 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePublic') . '</td>';
2575 print '<td class="valuefieldcreate">';
2576 $note_public = GETPOST('note_public', 'restricthtml');
2577 if (!GETPOSTISSET('note_public') && empty($note_public) && !empty($objectsrc)) {
2578 $note_public = $objectsrc->note_public;
2579 }
2580 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
2581 print $doleditor->Create(1);
2582
2583 // Private note
2584 if (empty($user->socid)) {
2585 print '<tr class="field_note_private">';
2586 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePrivate') . '</td>';
2587 print '<td class="valuefieldcreate">';
2588 $note_private = GETPOST('note_private', 'restricthtml');
2589 if (!GETPOSTISSET('note_private') && empty($note_private) && !empty($objectsrc)) {
2590 $note_private = $objectsrc->note_private;
2591 }
2592 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
2593 print $doleditor->Create(1);
2594 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
2595 print '</td></tr>';
2596 }
2597
2598 // Other attributes
2599 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php';
2600
2601 // Lines from source
2602 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2603 // TODO for compatibility
2604 if ($origin == 'contrat') {
2605 // Calcul contrat->price (HT), contrat->total (TTC), contrat->tva
2606 //$objectsrc->remise_absolue = $remise_absolue; // deprecated
2607 //$objectsrc->remise_percent = $remise_percent;
2608 $objectsrc->update_price(1, 'auto', 1);
2609 }
2610
2611 print "\n<!-- " . $classname . " info -->";
2612 print "\n";
2613 print '<input type="hidden" name="amount" value="' . $objectsrc->total_ht . '">' . "\n";
2614 print '<input type="hidden" name="total" value="' . $objectsrc->total_ttc . '">' . "\n";
2615 print '<input type="hidden" name="tva" value="' . $objectsrc->total_tva . '">' . "\n";
2616 print '<input type="hidden" name="origin" value="' . $objectsrc->element . '">';
2617 print '<input type="hidden" name="originid" value="' . $objectsrc->id . '">';
2618
2619 $newclassname = $classname;
2620 if ($newclassname == 'Propal') {
2621 $newclassname = 'CommercialProposal';
2622 } elseif ($newclassname == 'Commande') {
2623 $newclassname = 'Order';
2624 } elseif ($newclassname == 'Expedition') {
2625 $newclassname = 'Sending';
2626 } elseif ($newclassname == 'Fichinter') {
2627 $newclassname = 'Intervention';
2628 }
2629
2630 print '<tr><td>' . $langs->trans($newclassname) . '</td><td>' . $objectsrc->getNomUrl(1) . '</td></tr>';
2631 print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td></tr>';
2632 print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2633 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
2634 print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2635 }
2636
2637 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
2638 print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2639 }
2640 print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2641
2642 if (isModEnabled("multicurrency")) {
2643 print '<tr><td>' . $langs->trans('MulticurrencyAmountHT') . '</td><td>' . price($objectsrc->multicurrency_total_ht) . '</td></tr>';
2644 print '<tr><td>' . $langs->trans('MulticurrencyAmountVAT') . '</td><td>' . price($objectsrc->multicurrency_total_tva) . "</td></tr>";
2645 print '<tr><td>' . $langs->trans('MulticurrencyAmountTTC') . '</td><td>' . price($objectsrc->multicurrency_total_ttc) . "</td></tr>";
2646 }
2647 }
2648
2649 print "</table>\n";
2650
2651
2652 /*
2653 * Combobox for copy function
2654 */
2655
2656 if (!getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2657 print '<input type="hidden" name="createmode" value="empty">';
2658 }
2659
2660 if (getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2661 print '<br><table>';
2662
2663 // For backward compatibility
2664 print '<tr>';
2665 print '<td><input type="radio" name="createmode" value="copy"></td>';
2666 print '<td>' . $langs->trans("CopyPropalFrom") . ' </td>';
2667 print '<td>';
2668 $liste_propal = array();
2669 $liste_propal[0] = '';
2670
2671 $sql = "SELECT p.rowid as id, p.ref, s.nom";
2672 $sql .= " FROM " . MAIN_DB_PREFIX . "propal p";
2673 $sql .= ", " . MAIN_DB_PREFIX . "societe s";
2674 $sql .= " WHERE s.rowid = p.fk_soc";
2675 $sql .= " AND p.entity IN (" . getEntity('propal') . ")";
2676 $sql .= " AND p.fk_statut <> 0";
2677 $sql .= " ORDER BY Id";
2678
2679 $resql = $db->query($sql);
2680 if ($resql) {
2681 $num = $db->num_rows($resql);
2682 $i = 0;
2683 while ($i < $num) {
2684 $row = $db->fetch_row($resql);
2685 $propalRefAndSocName = $row[1] . " - " . $row[2];
2686 $liste_propal[$row[0]] = $propalRefAndSocName;
2687 $i++;
2688 }
2689 print $form->selectarray("copie_propal", $liste_propal, 0);
2690 } else {
2691 dol_print_error($db);
2692 }
2693 print '</td></tr>';
2694
2695 print '<tr><td class="tdtop"><input type="radio" name="createmode" value="empty" checked></td>';
2696 print '<td valign="top" colspan="2">' . $langs->trans("CreateEmptyPropal") . '</td></tr>';
2697 print '</table>';
2698 }
2699 }
2700
2701 print dol_get_fiche_end();
2702
2703 $langs->load("bills");
2704
2705 print $form->buttonsSaveCancel("CreateDraft");
2706
2707 print "</form>";
2708
2709
2710 // Show origin lines
2711 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2712 print '<br>';
2713
2714 $title = $langs->trans('ProductsAndServices');
2715 print load_fiche_titre($title);
2716
2717 print '<div class="div-table-responsive-no-min">';
2718 print '<table class="noborder centpercent">';
2719
2720 $objectsrc->printOriginLinesList();
2721
2722 print '</table>';
2723 print '</div>';
2724 }
2725} elseif ($object->id > 0) {
2726 /*
2727 * Show object in view mode
2728 */
2729 $object->fetch_thirdparty();
2730 if ($object->thirdparty) {
2731 $soc = $object->thirdparty;
2732 } else {
2733 $soc = new Societe($db);
2734 }
2735
2736 $head = propal_prepare_head($object);
2737 print dol_get_fiche_head($head, 'comm', $langs->trans('Proposal'), -1, $object->picto, 0, '', '', 0, '', 1);
2738
2739 $formconfirm = '';
2740
2741 // Clone confirmation
2742 if ($action == 'clone') {
2743 // Create an array for form
2744 $filter = '(s.client:IN:1,2,3)';
2745 $formquestion = array(
2746 // 'text' => $langs->trans("ConfirmClone"),
2747 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
2748 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOSTINT('socid'), 'socid', $filter, '', 0, 0, array(), 0, 'maxwidth300')),
2749 array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => 0),
2750 array('type' => 'checkbox', 'name' => 'update_desc', 'label' => $langs->trans('PuttingDescUpToDate'), 'value' => 0),
2751 );
2752 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY') && !empty($object->delivery_date)) {
2753 $formquestion[] = array('type' => 'date', 'name' => 'date_delivery', 'label' => $langs->trans("DeliveryDate"), 'value' => $object->delivery_date);
2754 }
2755 // Incomplete payment. We ask if reason = discount or other
2756 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ToClone'), $langs->trans('ConfirmClonePropal', $object->ref), 'confirm_clone', $formquestion, 'yes', 1, 0, 600);
2757 }
2758
2759 // Subtotal line form
2760 if ($action == 'add_title_line') {
2761 $langs->load('subtotals');
2762 $type = 'title';
2763 $depth_array = $object->getPossibleLevels($langs);
2764 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2765 } elseif ($action == 'add_subtotal_line') {
2766 $langs->load('subtotals');
2767 $type = 'subtotal';
2768 $titles = $object->getPossibleTitles();
2769 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2770 }
2771
2772 if ($action == 'closeas') {
2773 //Form to close proposal (signed or not)
2774 $formquestion = array();
2775 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2776 $formquestion[] = array('type' => 'select', 'name' => 'statut', 'label' => '<span class="fieldrequired">' . $langs->trans("CloseAs") . '</span>', 'values' => array($object::STATUS_SIGNED => $object->LibStatut($object::STATUS_SIGNED), $object::STATUS_NOTSIGNED => $object->LibStatut($object::STATUS_NOTSIGNED)));
2777 }
2778 $formquestion[] = array('type' => 'text', 'name' => 'note_private', 'label' => $langs->trans("Note"), 'value' => ''); // Field to complete private note (not replace)
2779
2780 if (getDolGlobalInt('PROPOSAL_SUGGEST_DOWN_PAYMENT_INVOICE_CREATION')) {
2781 // This is a hidden option:
2782 // Suggestion to create invoice during proposal signature is not enabled by default.
2783 // Such choice should be managed by the workflow module and trigger. This option generates conflicts with some setup.
2784 // It may also break step of creating an order when invoicing must be done from orders and not from proposal
2785 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
2786
2787 if (!empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
2788 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
2789
2790 $object->fetchObjectLinked();
2791
2792 $eligibleForDepositGeneration = true;
2793
2794 if (array_key_exists('facture', $object->linkedObjects)) {
2795 foreach ($object->linkedObjects['facture'] as $invoice) {
2796 '@phan-var-force Facture $invoice';
2798 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2799 $eligibleForDepositGeneration = false;
2800 break;
2801 }
2802 }
2803 }
2804
2805 if ($eligibleForDepositGeneration && array_key_exists('commande', $object->linkedObjects)) {
2806 foreach ($object->linkedObjects['commande'] as $order) {
2807 $order->fetchObjectLinked();
2808
2809 if (array_key_exists('facture', $order->linkedObjects)) {
2810 foreach ($order->linkedObjects['facture'] as $invoice) {
2811 '@phan-var-force Facture $invoice';
2813 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2814 $eligibleForDepositGeneration = false;
2815 break 2;
2816 }
2817 }
2818 }
2819 }
2820 }
2821
2822
2823 if ($eligibleForDepositGeneration) {
2824 $formquestion[] = array(
2825 'type' => 'checkbox',
2826 'tdclass' => 'showonlyifsigned',
2827 'name' => 'generate_deposit',
2828 'morecss' => 'margintoponly marginbottomonly',
2829 'label' => $form->textwithpicto($langs->trans('GenerateDeposit', $object->deposit_percent), $langs->trans('DepositGenerationPermittedByThePaymentTermsSelected'))
2830 );
2831
2832 $formquestion[] = array(
2833 'type' => 'date',
2834 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2835 'name' => 'datef',
2836 'label' => $langs->trans('DateInvoice'),
2837 'value' => dol_now(),
2838 'datenow' => true
2839 );
2840
2841 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2842 $formquestion[] = array(
2843 'type' => 'date',
2844 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2845 'name' => 'date_pointoftax',
2846 'label' => $langs->trans('DatePointOfTax'),
2847 'value' => dol_now(),
2848 'datenow' => true
2849 );
2850 }
2851
2852 $paymentTermsSelect = $form->getSelectConditionsPaiements(0, 'cond_reglement_id', -1, 0, 1, 'minwidth200');
2853
2854 $formquestion[] = array(
2855 'type' => 'other',
2856 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2857 'name' => 'cond_reglement_id',
2858 'label' => $langs->trans('PaymentTerm'),
2859 'value' => $paymentTermsSelect
2860 );
2861
2862 $formquestion[] = array(
2863 'type' => 'checkbox',
2864 'tdclass' => 'showonlyifgeneratedeposit',
2865 'name' => 'validate_generated_deposit',
2866 'morecss' => 'margintoponly marginbottomonly',
2867 'label' => $langs->trans('ValidateGeneratedDeposit')
2868 );
2869
2870 $formquestion[] = array(
2871 'type' => 'onecolumn',
2872 'value' => '
2873 <script>
2874 let signedValue = ' . $object::STATUS_SIGNED . ';
2875
2876 $(document).ready(function() {
2877 $("[name=generate_deposit]").change(function () {
2878 let $self = $(this);
2879 let $target = $(".showonlyifgeneratedeposit").parent(".tagtr");
2880
2881 if (! $self.parents(".tagtr").is(":hidden") && $self.is(":checked")) {
2882 $target.show();
2883 } else {
2884 $target.hide();
2885 }
2886
2887 return true;
2888 });
2889
2890 $("#statut").change(function() {
2891 let $target = $(".showonlyifsigned").parent(".tagtr");
2892
2893 if ($(this).val() == signedValue) {
2894 $target.show();
2895 } else {
2896 $target.hide();
2897 }
2898
2899 $("[name=generate_deposit]").trigger("change");
2900
2901 return true;
2902 });
2903
2904 $("#statut").trigger("change");
2905 });
2906 </script>
2907 '
2908 );
2909 }
2910 }
2911 }
2912
2913 if (isModEnabled('notification')) {
2914 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2915 $notify = new Notify($db);
2916 $formquestion = array_merge($formquestion, array(
2917 array('type' => 'onecolumn', 'value' => $notify->confirmMessage('PROPAL_CLOSE_SIGNED', $object->socid, $object)),
2918 ));
2919 }
2920
2921 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2922 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SetAcceptedRefused'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2923 } else {
2924 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?statut=3&id=' . $object->id, $langs->trans('Close'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2925 }
2926 } elseif ($action == 'cancel') {
2927 // Confirm cancel
2928 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans("CancelPropal"), $langs->trans('ConfirmCancelPropal', $object->ref), 'confirm_cancel', '', 0, 1);
2929 } elseif ($action == 'delete') {
2930 // Confirm delete
2931 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('DeleteProp'), $langs->trans('ConfirmDeleteProp', $object->ref), 'confirm_delete', '', 0, 1);
2932 } elseif ($action == 'reopen') {
2933 // Confirm reopen
2934 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ReOpen'), $langs->trans('ConfirmReOpenProp', $object->ref), 'confirm_reopen', '', 0, 1);
2935 } elseif ($action == 'ask_deleteline') {
2936 // Confirmation delete product/service line
2937 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1);
2938 } elseif ($action == 'ask_subtotal_deleteline') {
2939 // Confirmation de la suppression d'une ligne subtotal
2940 $langs->load("subtotals");
2941 $title = "DeleteSubtotalLine";
2942 $question = "ConfirmDeleteSubtotalLine";
2943 if (GETPOST('type') == 'title') {
2944 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2945 $title = "DeleteTitleLine";
2946 $question = "ConfirmDeleteTitleLine";
2947 }
2948 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2949 } elseif ($action == 'validate') {
2950 // Confirm validate proposal
2951 $error = 0;
2952
2953 // We verify whether the object is provisionally numbering
2954 $ref = substr($object->ref, 1, 4);
2955 if ($ref == 'PROV' || $ref == '') {
2956 $numref = $object->getNextNumRef($soc);
2957 if (empty($numref)) {
2958 $error++;
2959 setEventMessages($object->error, $object->errors, 'errors');
2960 }
2961 } else {
2962 $numref = (string) $object->ref;
2963 }
2964
2965 $text = $langs->trans('ConfirmValidateProp', $numref);
2966 if (isModEnabled('notification')) {
2967 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2968 $notify = new Notify($db);
2969 $text .= '<br>';
2970 $text .= $notify->confirmMessage('PROPAL_VALIDATE', $object->socid, $object);
2971 }
2972
2973 // mandatoryPeriod
2974 $nbMandated = 0;
2975 foreach ($object->lines as $line) {
2976 $res = $line->fetch_product();
2977 if ($res > 0) {
2978 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
2979 $nbMandated++;
2980 break;
2981 }
2982 }
2983 }
2984 if ($nbMandated > 0) {
2985 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
2986 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
2987 $error++;
2988 } else {
2989 $text .= '<div><span class="clearboth nowraponall warning">' . img_warning() . $langs->trans("mandatoryPeriodNeedTobeSetMsgValidate") . '</span></div>';
2990 }
2991 }
2992
2993 if (!$error) {
2994 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ValidateProp'), $text, 'confirm_validate', '', 0, 1, 240);
2995 }
2996 }
2997
2998 // Call Hook formConfirm
2999 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
3000 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3001 if (empty($reshook)) {
3002 $formconfirm .= $hookmanager->resPrint;
3003 } elseif ($reshook > 0) {
3004 $formconfirm = $hookmanager->resPrint;
3005 }
3006
3007 // Print form confirm
3008 print $formconfirm;
3009
3010
3011 // Proposal card
3012
3013 $linkback = '<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
3014
3015 $morehtmlref = '<div class="refidno">';
3016 // Ref customer
3017 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string', '', 0, 1);
3018 $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string' . (isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
3019 // Thirdparty
3020 $morehtmlref .= '<br>' . $soc->getNomUrl(1, 'customer');
3021 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $soc->id > 0) {
3022 $morehtmlref .= ' (<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?socid=' . $soc->id . '&search_societe=' . urlencode($soc->name) . '">' . $langs->trans("OtherProposals") . '</a>)';
3023 }
3024 // Project
3025 if (isModEnabled('project')) {
3026 $langs->load("projects");
3027 $morehtmlref .= '<br>';
3028 if ($usercancreate) {
3029 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
3030 if ($action != 'classify') {
3031 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
3032 }
3033 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
3034 } else {
3035 if (!empty($object->fk_project)) {
3036 $proj = new Project($db);
3037 $proj->fetch($object->fk_project);
3038 $morehtmlref .= $proj->getNomUrl(1);
3039 if ($proj->title) {
3040 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
3041 }
3042 }
3043 }
3044 }
3045 $morehtmlref .= '</div>';
3046
3047
3048 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
3049
3050 // Call Hook tabContentViewProposal
3051 $parameters = array();
3052 // Note that $action and $object may be modified by hook
3053 $reshook = $hookmanager->executeHooks('tabContentViewProposal', $parameters, $object, $action);
3054 if (empty($reshook)) {
3055 print '<div class="fichecenter">';
3056 print '<div class="fichehalfleft">';
3057 print '<div class="underbanner clearboth"></div>';
3058
3059 print '<table class="border tableforfield centpercent">';
3060
3061 // Link for thirdparty discounts
3062 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
3063 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3064 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3065 } else {
3066 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
3067 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
3068 }
3069
3070 print '<tr><td class="titlefieldmax45">' . $langs->trans('Discounts') . '</td><td>';
3071
3072 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
3073 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
3074 $absolute_discount = price2num($absolute_discount, 'MT');
3075 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
3076
3077 $caneditfield = ($object->status != Propal::STATUS_SIGNED && $object->status != Propal::STATUS_BILLED);
3078
3079 $thirdparty = $soc;
3080 $discount_type = 0;
3081 $backtopage = $_SERVER["PHP_SELF"] . '?id=' . $object->id;
3082 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
3083
3084 print '</td></tr>';
3085
3086 // Date of proposal
3087 print '<tr>';
3088 print '<td>';
3089 // print '<table class="nobordernopadding" width="100%"><tr><td>';
3090 // print $langs->trans('DatePropal');
3091 // print '</td>';
3092 // if ($action != 'editdate' && $usercancreate && $caneditfield) {
3093 // print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editdate&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->trans('SetDate'), 1).'</a></td>';
3094 // }
3095
3096 // print '</tr></table>';
3097 $editenable = $usercancreate && $caneditfield && $object->status == Propal::STATUS_DRAFT;
3098 print $form->editfieldkey("DatePropal", 'date', '', $object, (int) $editenable);
3099 print '</td><td class="valuefield">';
3100 if ($action == 'editdate' && $usercancreate && $caneditfield) {
3101 print '<form name="editdate" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3102 print '<input type="hidden" name="token" value="' . newToken() . '">';
3103 print '<input type="hidden" name="action" value="setdate">';
3104 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3105 print $form->selectDate($object->date, 're', 0, 0, 0, "editdate");
3106 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3107 print '</form>';
3108 } else {
3109 if ($object->date) {
3110 print dol_print_date($object->date, 'day');
3111 } else {
3112 print '&nbsp;';
3113 }
3114 }
3115 print '</td>';
3116
3117 // Date end proposal
3118 print '<tr>';
3119 print '<td>';
3120 print '<table class="nobordernopadding centpercent"><tr><td>';
3121 print $langs->trans('DateEndPropal');
3122 print '</td>';
3123 if ($action != 'editecheance' && $usercancreate && $caneditfield) {
3124 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editecheance&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetConditions'), 1) . '</a></td>';
3125 }
3126 print '</tr></table>';
3127 print '</td><td class="valuefield">';
3128 if ($action == 'editecheance' && $usercancreate && $caneditfield) {
3129 print '<form name="editecheance" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3130 print '<input type="hidden" name="token" value="' . newToken() . '">';
3131 print '<input type="hidden" name="action" value="setecheance">';
3132 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3133 print $form->selectDate($object->fin_validite, 'ech', 0, 0, 0, "editecheance");
3134 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3135 print '</form>';
3136 } else {
3137 if (!empty($object->fin_validite)) {
3138 print dol_print_date($object->fin_validite, 'day');
3139 if ($object->status == Propal::STATUS_VALIDATED && $object->fin_validite < ($now - getWarningDelay('propal', 'cloture'))) {
3140 print img_warning($langs->trans("Late"));
3141 }
3142 } else {
3143 print '&nbsp;';
3144 }
3145 }
3146 print '</td>';
3147 print '</tr>';
3148
3149 // Payment term
3150 print '<tr><td>';
3151 print '<table class="nobordernopadding centpercent"><tr><td>';
3152 print $langs->trans('PaymentConditionsShort');
3153 print '</td>';
3154 if ($action != 'editconditions' && $usercancreate && $caneditfield) {
3155 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editconditions&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetConditions'), 1) . '</a></td>';
3156 }
3157 print '</tr></table>';
3158 print '</td><td class="valuefield">';
3159 if ($action == 'editconditions' && $usercancreate && $caneditfield) {
3160 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'cond_reglement_id', 0, '', 1, $object->deposit_percent);
3161 } else {
3162 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'none', 0, '', 1, $object->deposit_percent);
3163 }
3164 print '</td>';
3165 print '</tr>';
3166
3167 // Payment mode
3168 print '<tr class="field_mode_reglement_id">';
3169 print '<td>';
3170 print '<table class="nobordernopadding centpercent"><tr><td>';
3171 print $langs->trans('PaymentMode');
3172 print '</td>';
3173 if ($action != 'editmode' && $usercancreate && $caneditfield) {
3174 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editmode&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetMode'), 1) . '</a></td>';
3175 }
3176 print '</tr></table>';
3177 print '</td><td class="valuefieldcreate">';
3178 if ($action == 'editmode' && $usercancreate && $caneditfield) {
3179 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
3180 } else {
3181 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'none');
3182 }
3183 print '</td></tr>';
3184
3185 // Delivery date
3186 print '<tr><td>';
3187 print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, (int) ($usercancreate && $caneditfield), 'datepicker');
3188 print '</td><td class="valuefieldedit">';
3189 print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, $usercancreate && $caneditfield, 'datepicker');
3190 print '</td>';
3191 print '</tr>';
3192
3193 // Delivery delay
3194 print '<tr class="fielddeliverydelay"><td>';
3195 print '<table class="nobordernopadding centpercent"><tr><td>';
3196 if (isModEnabled('order')) {
3197 print $form->textwithpicto($langs->trans('AvailabilityPeriod'), $langs->trans('AvailabilityPeriod') . ' (' . $langs->trans('AfterOrder') . ')');
3198 } else {
3199 print $langs->trans('AvailabilityPeriod');
3200 }
3201 print '</td>';
3202 if ($action != 'editavailability' && $usercancreate && $caneditfield) {
3203 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editavailability&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetAvailability'), 1) . '</a></td>';
3204 }
3205 print '</tr></table>';
3206 print '</td><td class="valuefield">';
3207 if ($action == 'editavailability' && $usercancreate && $caneditfield) {
3208 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'availability_id', 1);
3209 } else {
3210 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'none', 1);
3211 }
3212
3213 print '</td>';
3214 print '</tr>';
3215
3216 // Shipping Method
3217 if (isModEnabled("shipping")) {
3218 print '<tr><td>';
3219 print '<table class="nobordernopadding centpercent"><tr><td>';
3220 print $langs->trans('SendingMethod');
3221 print '</td>';
3222 if ($action != 'editshippingmethod' && $usercancreate && $caneditfield) {
3223 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editshippingmethod&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetShippingMode'), 1) . '</a></td>';
3224 }
3225 print '</tr></table>';
3226 print '</td><td class="valuefield">';
3227 if ($action == 'editshippingmethod' && $usercancreate && $caneditfield) {
3228 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'shipping_method_id', 1);
3229 } else {
3230 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'none');
3231 }
3232 print '</td>';
3233 print '</tr>';
3234 }
3235
3236 // Warehouse
3237 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
3238 $langs->load('stocks');
3239 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
3240 $formproduct = new FormProduct($db);
3241 print '<tr class="field_warehouse_id"><td>';
3242 $editenable = $usercancreate;
3243 print $form->editfieldkey("Warehouse", 'warehouse', '', $object, $editenable);
3244 print '</td><td class="valuefieldcreate">';
3245 if ($action == 'editwarehouse') {
3246 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'warehouse_id', 1);
3247 } else {
3248 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'none');
3249 }
3250 print '</td>';
3251 print '</tr>';
3252 }
3253
3254 // Origin of demand
3255 print '<tr><td>';
3256 print '<table class="nobordernopadding centpercent"><tr><td>';
3257 print $langs->trans('Source');
3258 print '</td>';
3259 if ($action != 'editdemandreason' && $usercancreate) {
3260 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdemandreason&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetDemandReason'), 1) . '</a></td>';
3261 }
3262 print '</tr></table>';
3263 print '</td><td class="valuefield">';
3264 if ($action == 'editdemandreason' && $usercancreate) {
3265 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'demand_reason_id', 1);
3266 } else {
3267 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'none');
3268 }
3269 print '</td>';
3270 print '</tr>';
3271
3272 if ($soc->outstanding_limit) {
3273 // Outstanding Bill
3274 print '<tr><td>';
3275 print $langs->trans('OutstandingBill');
3276 print '</td><td class="valuefield">';
3277 $arrayoutstandingbills = $soc->getOutstandingBills();
3278 print($arrayoutstandingbills['opened'] > $soc->outstanding_limit ? img_warning() : '');
3279 print price($arrayoutstandingbills['opened']) . ' / ';
3280 print price($soc->outstanding_limit, 0, $langs, 1, -1, -1, $conf->currency);
3281 print '</td>';
3282 print '</tr>';
3283 }
3284
3285 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
3286 // Bank Account
3287 print '<tr><td>';
3288 print '<table width="100%" class="nobordernopadding"><tr><td>';
3289 print $langs->trans('BankAccount');
3290 print '</td>';
3291 if ($action != 'editbankaccount' && $usercancreate) {
3292 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editbankaccount&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetBankAccount'), 1) . '</a></td>';
3293 }
3294 print '</tr></table>';
3295 print '</td><td class="valuefield">';
3296 if ($action == 'editbankaccount') {
3297 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'fk_account', 1);
3298 } else {
3299 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'none');
3300 }
3301 print '</td>';
3302 print '</tr>';
3303 }
3304
3305 if (!getDolGlobalString('PROPOSAL_HIDE_CALCULATED_WEIGHT_VOLUME')) {
3306 $tmparray = $object->getTotalWeightVolume();
3307 $totalWeight = isset($tmparray['weight']) ? $tmparray['weight'] : 0;
3308 $totalVolume = isset($tmparray['volume']) ? $tmparray['volume'] : 0;
3309 if ($totalWeight) {
3310 print '<tr><td>' . $langs->trans("CalculatedWeight") . '</td>';
3311 print '<td class="valuefield">';
3312 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'), 1);
3313 print '</td></tr>';
3314 }
3315 if ($totalVolume) {
3316 print '<tr><td>' . $langs->trans("CalculatedVolume") . '</td>';
3317 print '<td class="valuefield">';
3318 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'), 1);
3319 print '</td></tr>';
3320 }
3321 }
3322
3323 // Incoterms
3324 if (isModEnabled('incoterm')) {
3325 print '<tr><td>';
3326 print '<table width="100%" class="nobordernopadding"><tr><td>';
3327 print $langs->trans('IncotermLabel');
3328 print '<td><td class="right">';
3329 if ($action != 'editincoterm' && $usercancreate && $caneditfield) {
3330 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3331 } else {
3332 print '&nbsp;';
3333 }
3334 print '</td></tr></table>';
3335 print '</td>';
3336 print '<td class="valuefield">';
3337 if ($action == 'editincoterm' && $usercancreate && $caneditfield) {
3338 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3339 } else {
3340 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3341 }
3342 print '</td></tr>';
3343 }
3344
3345 // Categories
3346 if (isModEnabled('category')) {
3347 print '<tr><td>';
3348 print '<table class="nobordernopadding centpercent"><tr><td>';
3349 print $langs->trans("Categories");
3350 print '<td><td class="right">';
3351 if ($usercancreate) {
3352 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/comm/propal/card.php?id='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
3353 } else {
3354 print '&nbsp;';
3355 }
3356 print '</td></tr></table>';
3357 print '</td>';
3358 print '<td>';
3359 if ($action == 'edittags') {
3360 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
3361 print '<input type="hidden" name="action" value="settags">';
3362 print '<input type="hidden" name="token" value="'.newToken().'">';
3363 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
3364 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
3365 print '</form>';
3366 } else {
3367 print $form->showCategories($object->id, Categorie::TYPE_PROPOSAL, 1);
3368 }
3369 print "</td></tr>";
3370 }
3371
3372 // Other attributes
3373 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3374
3375 print '</table>';
3376
3377 print '</div>';
3378 print '<div class="fichehalfright">';
3379 print '<div class="underbanner clearboth"></div>';
3380
3381 print '<table class="border tableforfield centpercent">';
3382
3383 include DOL_DOCUMENT_ROOT . '/core/tpl/object_currency_amount.tpl.php';
3384
3385 print '<tr>';
3386 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
3387 print '<td class="nowrap amountcard right">' . price($object->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3388 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3389 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ht, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3390 }
3391 print '</tr>';
3392
3393 print '<tr>';
3394 print '<td>' . $langs->trans('AmountVAT') . '</td>';
3395 print '<td class="nowrap amountcard right">' . price($object->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3396 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3397 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_tva, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3398 }
3399 print '</tr>';
3400
3401 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
3402 print '<tr>';
3403 print '<td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
3404 print '<td class="nowrap amountcard right">' . price($object->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3405 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3406 $object->multicurrency_total_localtax1 = price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
3407
3408 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax1, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3409 }
3410 print '</tr>';
3411 }
3412
3413 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
3414 print '<tr>';
3415 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
3416 print '<td class="nowrap amountcard right">' . price($object->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3417 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3418 $object->multicurrency_total_localtax2 = price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
3419
3420 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax2, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3421 }
3422 print '</tr>';
3423 }
3424
3425 print '<tr>';
3426 print '<td>' . $langs->trans('AmountTTC') . '</td>';
3427 print '<td class="nowrap amountcard right">' . price($object->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3428 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3429 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3430 }
3431 print '</tr>';
3432
3433 print '</table>';
3434
3435 // Margin Infos
3436 if (isModEnabled('margin')) {
3437 $formmargin->displayMarginInfos($object);
3438 }
3439
3440 print '</div>';
3441 print '</div>';
3442
3443 print '<div class="clearboth"></div><br>';
3444
3445 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3446 $blocname = 'contacts';
3447 $title = $langs->trans('ContactsAddresses');
3448 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3449 }
3450
3451 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
3452 $blocname = 'notes';
3453 $title = $langs->trans('Notes');
3454 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3455 }
3456
3457 /*
3458 * Lines
3459 */
3460
3461 // Get object lines
3462 $result = $object->getLinesArray();
3463
3464 // Add products/services form
3465 //$forceall = 1;
3466 global $inputalsopricewithtax;
3467 $inputalsopricewithtax = 1;
3468
3469 print ' <form name="addproduct" id="addproduct" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
3470 <input type="hidden" name="token" value="' . newToken() . '">
3471 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
3472 <input type="hidden" name="mode" value="">
3473 <input type="hidden" name="page_y" value="">
3474 <input type="hidden" name="backtopage" value="' . $backtopage . '">
3475 <input type="hidden" name="id" value="' . $object->id . '">
3476 ';
3477
3478 if (!empty($conf->use_javascript_ajax) && $object->status == Propal::STATUS_DRAFT) {
3479 if (isModEnabled('subtotals')) {
3480 include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php';
3481 } else {
3482 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3483 }
3484 }
3485
3486 print '<div class="div-table-responsive-no-min">';
3487 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3488 print '<table id="tablelines" class="noborder noshadow centpercent">';
3489 }
3490
3491 if (!empty($object->lines)) {
3492 $object->printObjectLines($action, $mysoc, $object->thirdparty, $lineid, 1);
3493 }
3494
3495 // Form to add new line
3496 if ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines') {
3497 if ($action != 'editline') {
3498 $parameters = array();
3499 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3500 if ($reshook < 0) {
3501 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3502 }
3503 if (empty($reshook)) {
3504 $object->formAddObjectLine(1, $mysoc, $soc);
3505 }
3506 } else {
3507 $parameters = array();
3508 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3509 }
3510 }
3511
3512 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3513 print '</table>';
3514 }
3515 print '</div>';
3516
3517 print "</form>\n";
3518 }
3519
3520 print dol_get_fiche_end();
3521
3522
3523 /*
3524 * Button Actions
3525 */
3526
3527 if ($action != 'presend') {
3528 $numlines = count($object->lines);
3529 print '<div class="tabsAction">';
3530
3531 $parameters = array();
3532 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3533 // modified by hook
3534 if (empty($reshook)) {
3535 if ($action != 'editline') {
3536 // Subtotal
3537 if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals')
3538 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
3539 $langs->load('subtotals');
3540
3541 $url_button = array();
3542
3543 $url_button[] = array(
3544 'lang' => 'subtotals',
3545 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
3546 'perm' => (bool) $usercancreate,
3547 'label' => $langs->trans('AddTitleLine'),
3548 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_title_line&token=' . newToken()
3549 );
3550
3551 $url_button[] = array(
3552 'lang' => 'subtotals',
3553 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
3554 'perm' => (bool) $usercancreate,
3555 'label' => $langs->trans('AddSubtotalLine'),
3556 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_subtotal_line&token=' . newToken()
3557 );
3558
3559 print dolGetButtonAction('', $langs->trans('Subtotal'), 'default', $url_button, '', true);
3560 }
3561
3562 // Validate
3563 if (($object->status == Propal::STATUS_DRAFT && $object->total_ttc >= 0 && count($object->lines) > 0)
3564 || ($object->status == Propal::STATUS_DRAFT && getDolGlobalString('PROPAL_ENABLE_NEGATIVE') && count($object->lines) > 0)
3565 ) {
3566 if ($usercanvalidate) {
3567 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=validate&token=' . newToken() . '">' . (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE') ? $langs->trans('Validate') : $langs->trans('ValidateAndSign')) . '</a>';
3568 } else {
3569 print '<a class="butActionRefused classfortooltip" href="#">' . $langs->trans('Validate') . '</a>';
3570 }
3571 }
3572 // Create event
3573 /*if (isModEnabled('agenda') && !empty($conf->global->MAIN_ADD_EVENT_ON_ELEMENT_CARD)) // Add hidden condition because this is not a "workflow" action so should appears somewhere else on page.
3574 {
3575 print '<a class="butAction" href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;origin=' . $object->element . '&amp;originid=' . $object->id . '&amp;socid=' . $object->socid . '">' . $langs->trans("AddAction") . '</a></div>';
3576 }*/
3577 // Edit
3578 if ($object->status == Propal::STATUS_VALIDATED && $usercancreate) {
3579 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=modif&token=' . newToken() . '">' . $langs->trans('Modify') . '</a>';
3580 }
3581
3582 // ReOpen
3583 if (((getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && $object->status == Propal::STATUS_NOTSIGNED) || (!getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && ($object->status == Propal::STATUS_SIGNED || $object->status == Propal::STATUS_NOTSIGNED || $object->status == Propal::STATUS_BILLED || $object->status == Propal::STATUS_CANCELED)))) {
3584 if ($usercanreopen) {
3585 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=reopen&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#reopen') . '"';
3586 print '>' . $langs->trans('ReOpen') . '</a>';
3587 } else {
3588 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ReOpen") . '</a>';
3589 }
3590 }
3591
3592 // Send
3593 if (empty($user->socid)) {
3594 if ($object->status == Propal::STATUS_VALIDATED || $object->status == Propal::STATUS_SIGNED || getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
3595 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '', $usercansend);
3596 }
3597 }
3598
3599 $arrayforbutaction = array();
3600
3601 // Create a sale order
3602 $arrayforbutaction[] = array(
3603 'lang' => 'orders',
3604 'enabled' => (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED),
3605 'perm' => $usercancreateorder,
3606 'label' => 'AddOrder',
3607 'url' => '/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3608 );
3609 /*if (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED) {
3610 if ($usercancreateorder) {
3611 print '<a class="butAction" href="'.DOL_URL_ROOT.'/commande/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddOrder").'</a>';
3612 }
3613 }*/
3614
3615 // Create a purchase order
3616 if (getDolGlobalString('WORKFLOW_CAN_CREATE_PURCHASE_ORDER_FROM_PROPOSAL')) {
3617 $arrayforbutaction[] = array(
3618 'lang' => 'orders',
3619 'enabled' => ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")),
3620 'perm' => $usercancreatepurchaseorder,
3621 'label' => 'AddPurchaseOrder',
3622 'url' => '/fourn/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3623 );
3624 /*if ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")) {
3625 if ($usercancreatepurchaseorder) {
3626 print '<a class="butAction" href="'.DOL_URL_ROOT.'/fourn/commande/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddPurchaseOrder").'</a>';
3627 }
3628 }*/
3629 }
3630
3631 // Create an intervention
3632 $arrayforbutaction[] = array(
3633 'lang' => 'interventions',
3634 'enabled' => (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED),
3635 'perm' => $usercancreateintervention,
3636 'label' => 'AddIntervention',
3637 'url' => '/fichinter/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3638 );
3639 /*if (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED) {
3640 if ($usercancreateintervention) {
3641 $langs->load("interventions");
3642 print '<a class="butAction" href="'.DOL_URL_ROOT.'/fichinter/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddIntervention").'</a>';
3643 }
3644 }*/
3645
3646 // Create contract
3647 $arrayforbutaction[] = array(
3648 'lang' => 'contracts',
3649 'enabled' => (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED),
3650 'perm' => $usercancreatecontract,
3651 'label' => 'AddContract',
3652 'url' => '/contrat/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3653 );
3654 /*if (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED) {
3655 $langs->load("contracts");
3656
3657 if ($usercancreatecontract) {
3658 print '<a class="butAction" href="'.DOL_URL_ROOT.'/contrat/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans('AddContract').'</a>';
3659 }
3660 }*/
3661
3662 // Create an invoice and classify billed
3663 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3664 $arrayforbutaction[] = [
3665 'lang' => 'invoice',
3666 'enabled' => isModEnabled('invoice'),
3667 'perm' => $usercancreateinvoice,
3668 'label' => 'CreateBill',
3669 'url' => '/compta/facture/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid),
3670 ];
3671 /*if (isModEnabled('invoice') && $usercancreateinvoice) {
3672 print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("CreateBill").'</a>';
3673 }*/
3674 }
3675
3676 $actionButtonsParameters = [
3677 "areDropdownButtons" => !getDolGlobalInt("MAIN_REMOVE_DROPDOWN_CREATE_BUTTONS_ON_ORDER"),
3678 "backtopage" => $_SERVER["PHP_SELF"] . "?id=" . ((int) $id)
3679 ];
3680
3681 if ($numlines > 0) {
3682 print dolGetButtonAction('', $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 1, $actionButtonsParameters);
3683 } else {
3684 print dolGetButtonAction($langs->trans("ErrorObjectMustHaveLinesToBeValidated", $object->ref), $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 0, $actionButtonsParameters);
3685 }
3686
3687 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3688 $arrayofinvoiceforpropal = $object->getInvoiceArrayList();
3689 if ((is_array($arrayofinvoiceforpropal) && count($arrayofinvoiceforpropal) > 0) || !getDolGlobalString('WORKFLOW_PROPAL_NEED_INVOICE_TO_BE_CLASSIFIED_BILLED')) {
3690 if ($usercanclose) {
3691 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=classifybilled&token=' . newToken() . '&socid=' . $object->socid . '">' . $langs->trans("ClassifyBilled") . '</a>';
3692 } else {
3693 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ClassifyBilled") . '</a>';
3694 }
3695 }
3696 }
3697
3698 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
3699 // Close as accepted/refused
3700 if ($object->status == Propal::STATUS_VALIDATED) {
3701 if ($usercanclose) {
3702 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3703 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3704 } else {
3705 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '"';
3706 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3707 }
3708 }
3709 } else {
3710 // Set not signed (close)
3711 if ($object->status == Propal::STATUS_DRAFT && $usercanclose) {
3712 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&token=' . newToken() . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3713 print '>' . $langs->trans('SetRefusedAndClose') . '</a>';
3714 }
3715 }
3716
3717 // Cancel propal
3718 if ($object->status > Propal::STATUS_DRAFT && $usercanclose) {
3719 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=cancel&token=' . newToken() . '">' . $langs->trans("CancelPropal") . '</a>';
3720 }
3721
3722 // Clone
3723 if ($usercancreate) {
3724 print '<a class="butAction" href="' . $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&socid=' . $object->socid . '&action=clone&token=' . newToken() . '&object=' . $object->element . '">' . $langs->trans("ToClone") . '</a>';
3725 }
3726
3727 // Delete
3728 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=delete&token=' . newToken(), 'delete', $usercandelete);
3729 }
3730 }
3731
3732 print '</div>';
3733 }
3734
3735 //Select mail models is same action as presend
3736 if (GETPOST('modelselected')) {
3737 $action = 'presend';
3738 }
3739
3740 if ($action != 'presend') {
3741 print '<div class="fichecenter"><div class="fichehalfleft">';
3742 print '<a name="builddoc"></a>'; // ancre
3743 /*
3744 * Generated documents
3745 */
3746 $objref = dol_sanitizeFileName($object->ref);
3747 $filedir = $conf->propal->multidir_output[$object->entity ?? $conf->entity] . "/" . dol_sanitizeFileName($object->ref);
3748 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3749 $genallowed = $usercanread;
3750 $delallowed = $usercancreate;
3751 $tooltipAfterComboOfModels = '';
3752 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_PROPAL')) {
3753 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_PROPAL_TERMSOFSALE'));
3754 }
3755
3756 print $formfile->showdocuments('propal', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '0', '', $soc->default_lang, '', $object, 0, 'remove_file', $tooltipAfterComboOfModels);
3757
3758 // Show links to link elements
3759 $tmparray = $form->showLinkToObjectBlock($object, array(), array('propal'), 1);
3760 $linktoelem = $tmparray['linktoelem'];
3761 $htmltoenteralink = $tmparray['htmltoenteralink'];
3762 print $htmltoenteralink;
3763
3764 $compatibleImportElementsList = false;
3765 if ($user->hasRight('propal', 'creer') && $object->status == Propal::STATUS_DRAFT) {
3766 $compatibleImportElementsList = array('commande', 'propal', 'facture', 'subscription'); // import from linked elements
3767 }
3768 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
3769
3770 // Show online signature link
3771 $useonlinesignature = getDolGlobalInt('PROPOSAL_ALLOW_ONLINESIGN');
3772
3773 if ($object->status != Propal::STATUS_DRAFT && $useonlinesignature) {
3774 print '<br><!-- Link to sign -->';
3775 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3776 print showOnlineSignatureUrl('proposal', $object->ref, $object) . '<br>';
3777 }
3778
3779 print '</div><div class="fichehalfright">';
3780
3781 $MAXEVENT = 10;
3782
3783 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/comm/propal/messaging.php?id=' . $object->id);
3784 $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT . '/comm/propal/agenda.php?id=' . $object->id);
3785
3786 // List of actions on element
3787 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3788 $formactions = new FormActions($db);
3789 $somethingshown = $formactions->showactions($object, 'propal', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
3790
3791 print '</div></div>';
3792 }
3793
3794 // Presend form
3795 $modelmail = 'propal_send';
3796 $defaulttopic = 'SendPropalRef';
3797 $diroutput = $conf->propal->multidir_output[$object->entity ?? $conf->entity];
3798 $trackid = 'pro' . $object->id;
3799
3800 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3801}
3802
3803// End of page
3804llxFooter();
3805$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage a WYSIWYG editor.
Class to manage standard extra fields.
static createDepositFromOrigin(CommonObject $origin, $date, $payment_terms_id, User $user, $notrigger=0, $autoValidateDeposit=false, $overrideFields=array())
Creates a deposit from a proposal or an order by grouping lines by VAT rates.
const TYPE_DEPOSIT
Deposit invoice.
Class to manage building of HTML components.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class permettant la generation de composants html autre Only common components are here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage building of HTML components.
Class to manage generation of HTML components for proposal management.
static liste_modeles($db, $maxfilenamelength=0)
Return list of active generation modules.
Class to manage the table of subscription to notifications.
Class ProductCombination Used to represent the relation between a product and one of its variants.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
Class to manage projects.
Class to manage proposals.
const STATUS_DRAFT
Draft status.
const STATUS_SIGNED
Signed quote.
const STATUS_NOTSIGNED
Not signed quote.
const STATUS_BILLED
Billed or processed quote.
const STATUS_CANCELED
Canceled status.
const STATUS_VALIDATED
Validated status.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getWarningDelay($module, $parmlevel1, $parmlevel2='')
Return a warning delay You can use it like this: if (getWarningDelay('module', 'paramlevel1')) It rep...
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
getDictionaryValue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
Return the value of a filed into a dictionary for the record $id.
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
propal_prepare_head($object)
Prepare array with list of tabs.
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:464
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.