dolibarr 23.0.4
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) && preg_match('/^[a-zA-Z]+$/', $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 } elseif ($line->fk_product) { // Display errors only for non-free lines
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 if (GETPOSTISSET('shipping_method_id')) {
2340 $shipping_method_id = GETPOSTINT('shipping_method_id');
2341 }
2342 }
2343 // Warehouse default if null
2344 if ($soc->fk_warehouse > 0) {
2345 $warehouse_id = $soc->fk_warehouse;
2346 }
2347 if (isModEnabled('stock') && $warehouse_id < 0 && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2348 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
2349 $warehouse_id = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
2350 }
2351 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && !empty($user->fk_warehouse)) {
2352 $warehouse_id = $user->fk_warehouse;
2353 }
2354 }
2355
2356 print '<form name="addprop" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
2357 print '<input type="hidden" name="token" value="' . newToken() . '">';
2358 print '<input type="hidden" name="action" value="add">';
2359 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
2360 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
2361 if ($origin != 'project' && $originid) {
2362 print '<input type="hidden" name="origin" value="' . $origin . '">';
2363 print '<input type="hidden" name="originid" value="' . $originid . '">';
2364 if ($origin == 'contrat' && !empty($renewal)) {
2365 print '<input type="hidden" name="renewal" value="' . $renewal . '">';
2366 }
2367 } elseif ($origin == 'project' && !empty($projectid)) {
2368 print '<input type="hidden" name="projectid" value="' . $projectid . '">';
2369 }
2370
2371 print dol_get_fiche_head();
2372
2373 // Call Hook tabContentCreateProposal
2374 $parameters = array();
2375 // Note that $action and $object may be modified by hook
2376 $reshook = $hookmanager->executeHooks('tabContentCreateProposal', $parameters, $object, $action);
2377 if (empty($reshook)) {
2378 print '<table class="border centpercent">';
2379
2380 // Reference
2381 print '<tr class="field_ref"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Ref') . '</td><td class="valuefieldcreate">' . $langs->trans("Draft") . '</td></tr>';
2382
2383 // Ref customer
2384 print '<tr class="field_ref_client"><td class="titlefieldcreate">' . $langs->trans('RefCustomer') . '</td><td class="valuefieldcreate">';
2385 print '<input type="text" name="ref_client" value="' . (!empty($ref_client) ? $ref_client : GETPOST('ref_client')) . '"></td>';
2386 print '</tr>';
2387
2388 // Third party
2389 print '<tr class="field_socid">';
2390
2391 print '<td class="titlefieldcreate fieldrequired">' . $langs->trans('Customer') . '</td>';
2392 $shipping_method_id = 0;
2393
2394 if ($socid > 0) {
2395 print '<td class="valuefieldcreate">';
2396 print $soc->getNomUrl(1, 'customer');
2397 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
2398 print '</td>';
2399 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2400 $shipping_method_id = $soc->shipping_method_id;
2401 }
2402 } else {
2403 print '<td class="valuefieldcreate">';
2404 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
2405 print img_picto('', 'company', 'class="pictofixedwidth"') . $form->select_company('', 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 maxwidth500 widthcentpercentminusxx');
2406 // reload page to retrieve customer information
2407 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
2408 print '<script>
2409 $(document).ready(function() {
2410 $("#socid").change(function() {
2411 console.log("We have changed the company - Reload page");
2412 var socid = $(this).val();
2413 // reload page
2414 $("input[name=action]").val("create");
2415 $("input[name=changecompany]").val("1");
2416 $("form[name=addprop]").submit();
2417 });
2418 });
2419 </script>';
2420 }
2421 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>';
2422 print '</td>';
2423 }
2424 print '</tr>' . "\n";
2425
2426 if ($socid > 0) {
2427 // Contacts (ask contact only if thirdparty already defined).
2428 print '<tr class="field_contactid"><td class="titlefieldcreate">' . $langs->trans("DefaultContact") . '</td><td class="valuefieldcreate">';
2429 print img_picto('', 'contact', 'class="pictofixedwidth"');
2430 //print $form->selectcontacts($soc->id, $contactid, 'contactid', 1, '', '', 0, 'minwidth300 widthcentpercentminusx');
2431 print $form->select_contact($soc->id, $contactid, 'contactid', 1, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
2432 print '</td></tr>';
2433
2434 // Third party discounts info line
2435 print '<tr class="field_discount_info"><td class="titlefieldcreate">' . $langs->trans('Discounts') . '</td><td class="valuefieldcreate">';
2436
2437 $absolute_discount = $soc->getAvailableDiscounts();
2438
2439 $thirdparty = $soc;
2440 $discount_type = 0;
2441 $backtopage = $_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . urlencode((string) (GETPOST('origin'))) . '&originid=' . urlencode((string) (GETPOSTINT('originid')));
2442 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
2443 print '</td></tr>';
2444 }
2445
2446 $newdatepropal = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
2447 // Date
2448 print '<tr class="field_addprop"><td class="titlefieldcreate fieldrequired">' . $langs->trans('DatePropal') . '</td><td class="valuefieldcreate">';
2449 print img_picto('', 'action', 'class="pictofixedwidth"');
2450 print $form->selectDate($newdatepropal ? $newdatepropal : $datepropal, '', 0, 0, 0, "addprop", 1, 1);
2451 print '</td></tr>';
2452
2453 // Validaty duration
2454 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>';
2455
2456 // Terms of payment
2457 print '<tr class="field_cond_reglement_id"><td class="nowrap">' . $langs->trans('PaymentConditionsShort') . '</td><td>';
2458 print img_picto('', 'payment', 'class="pictofixedwidth"');
2459 // at last resort we take the payment term id which may be filled by default values set (if not getpostisset)
2460 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', 1, 1, 0, '', $deposit_percent);
2461 print '</td></tr>';
2462
2463 // Mode of payment
2464 print '<tr class="field_mode_reglement_id"><td class="titlefieldcreate">' . $langs->trans('PaymentMode') . '</td><td class="valuefieldcreate">';
2465 print img_picto('', 'bank', 'class="pictofixedwidth"');
2466 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth200 widthcentpercentminusx', 1);
2467 print '</td></tr>';
2468
2469 // Bank Account
2470 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
2471 print '<tr class="field_fk_account"><td class="titlefieldcreate">' . $langs->trans('BankAccount') . '</td><td class="valuefieldcreate">';
2472 print img_picto('', 'bank_account', 'class="pictofixedwidth"') . $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth200 widthcentpercentminusx', 1);
2473 print '</td></tr>';
2474 }
2475
2476 // Source / Channel - What trigger creation
2477 print '<tr class="field_demand_reason_id"><td class="titlefieldcreate">' . $langs->trans('Source') . '</td><td class="valuefieldcreate">';
2478 print img_picto('', 'question', 'class="pictofixedwidth"');
2479 $form->selectInputReason((GETPOSTISSET('demand_reason_id') ? GETPOSTINT('demand_reason_id') : ''), 'demand_reason_id', "SRC_PROP", 1, 'maxwidth200 widthcentpercentminusx');
2480 print '</td></tr>';
2481
2482 // Shipping Method
2483 if (isModEnabled("shipping")) {
2484 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2485 $shipping_method_id = $soc->shipping_method_id;
2486 }
2487 print '<tr class="field_shipping_method_id"><td class="titlefieldcreate">' . $langs->trans('SendingMethod') . '</td><td class="valuefieldcreate">';
2488 print img_picto('', 'dolly', 'class="pictofixedwidth"');
2489 $form->selectShippingMethod((string) $shipping_method_id, 'shipping_method_id', '', 1, '', 0, 'maxwidth200 widthcentpercentminusx');
2490 print '</td></tr>';
2491 }
2492
2493 $formproduct = null;
2494 // Warehouse
2495 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2496 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
2497 $formproduct = new FormProduct($db);
2498 print '<tr class="field_warehouse_id"><td class="titlefieldcreate">' . $langs->trans('Warehouse') . '</td><td class="valuefieldcreate">';
2499 print img_picto('', 'stock', 'class="pictofixedwidth"') . $formproduct->selectWarehouses((int) $warehouse_id, 'warehouse_id', '', 1, 0, 0, '', 0, 0, array(), 'maxwidth500 widthcentpercentminusxx');
2500 print '</td></tr>';
2501 }
2502
2503 // Delivery delay
2504 print '<tr class="field_availability_id"><td class="titlefieldcreate">' . $langs->trans('AvailabilityPeriod');
2505 if (isModEnabled('order')) {
2506 print ' (' . $langs->trans('AfterOrder') . ')';
2507 }
2508 print '</td><td class="valuefieldcreate">';
2509 print img_picto('', 'clock', 'class="pictofixedwidth"');
2510 $form->selectAvailabilityDelay((GETPOSTISSET('availability_id') ? GETPOSTINT('availability_id') : ''), 'availability_id', '', 1, 'maxwidth200 widthcentpercentminusx');
2511 print '</td></tr>';
2512
2513 // Delivery date (or manufacturing)
2514 print '<tr class="field_date_livraison"><td class="titlefieldcreate">' . $langs->trans("DeliveryDate") . '</td>';
2515 print '<td class="valuefieldcreate">';
2516 print img_picto('', 'action', 'class="pictofixedwidth"');
2517 if (is_numeric(getDolGlobalString('DATE_LIVRAISON_WEEK_DELAY'))) { // If value set to 0 or a num, not empty
2518 $tmpdte = time() + (7 * getDolGlobalInt('DATE_LIVRAISON_WEEK_DELAY') * 24 * 60 * 60);
2519 $syear = date("Y", $tmpdte);
2520 $smonth = date("m", $tmpdte);
2521 $sday = date("d", $tmpdte);
2522 print $form->selectDate($syear . "-" . $smonth . "-" . $sday, 'date_livraison', 0, 0, 0, "addprop");
2523 } else {
2524 $tmp_date_delivery = GETPOST('date_delivery') ?: -1;
2525 print $form->selectDate($tmp_date_delivery, 'date_livraison', 0, 0, 0, "addprop", 1, 1);
2526 }
2527 print '</td></tr>';
2528
2529 // Project
2530 if (isModEnabled('project') && is_object($formproject)) {
2531 $langs->load("projects");
2532 print '<tr class="field_projectid">';
2533 print '<td class="titlefieldcreate">'.$langs->trans("Project").'</td><td class="valuefieldcreate">';
2534 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');
2535 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>';
2536 print '</td>';
2537 print '</tr>';
2538 }
2539
2540 // Incoterms
2541 if (isModEnabled('incoterm')) {
2542 print '<tr class="field_incoterm_id">';
2543 print '<td class="titlefieldcreate"><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $soc->label_incoterms, 1) . '</label></td>';
2544 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2545 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
2546 print $form->select_incoterms((!empty($soc->fk_incoterms) ? $soc->fk_incoterms : ''), (!empty($soc->location_incoterms) ? $soc->location_incoterms : ''));
2547 print '</td></tr>';
2548 }
2549 if (isModEnabled('category')) {
2550 // Categories
2551 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
2552 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
2553 print "</td></tr>";
2554 }
2555 // Template to use by default
2556 print '<tr class="field_model">';
2557 print '<td class="titlefieldcreate">' . $langs->trans("DefaultModel") . '</td>';
2558 print '<td class="valuefieldcreate">';
2559 print img_picto('', 'pdf', 'class="pictofixedwidth"');
2561 $preselected = getDolGlobalString('PROPALE_ADDON_PDF_ODT_DEFAULT', getDolGlobalString("PROPALE_ADDON_PDF"));
2562 print $form->selectarray('model', $liste, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
2563 print "</td></tr>";
2564
2565 // Multicurrency
2566 if (isModEnabled("multicurrency")) {
2567 print '<tr class="field_currency">';
2568 print '<td class="titlefieldcreate">' . $form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0) . '</td>';
2569 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2570 print img_picto('', 'currency', 'class="pictofixedwidth"') . $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
2571 print '</td></tr>';
2572 }
2573
2574 // Public note
2575 print '<tr class="field_note_public">';
2576 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePublic') . '</td>';
2577 print '<td class="valuefieldcreate">';
2578 $note_public = GETPOST('note_public', 'restricthtml');
2579 if (!GETPOSTISSET('note_public') && empty($note_public) && !empty($objectsrc)) {
2580 $note_public = $objectsrc->note_public;
2581 }
2582 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
2583 print $doleditor->Create(1);
2584
2585 // Private note
2586 if (empty($user->socid)) {
2587 print '<tr class="field_note_private">';
2588 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePrivate') . '</td>';
2589 print '<td class="valuefieldcreate">';
2590 $note_private = GETPOST('note_private', 'restricthtml');
2591 if (!GETPOSTISSET('note_private') && empty($note_private) && !empty($objectsrc)) {
2592 $note_private = $objectsrc->note_private;
2593 }
2594 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
2595 print $doleditor->Create(1);
2596 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
2597 print '</td></tr>';
2598 }
2599
2600 // Other attributes
2601 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php';
2602
2603 // Lines from source
2604 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2605 // TODO for compatibility
2606 if ($origin == 'contrat') {
2607 // Calcul contrat->price (HT), contrat->total (TTC), contrat->tva
2608 //$objectsrc->remise_absolue = $remise_absolue; // deprecated
2609 //$objectsrc->remise_percent = $remise_percent;
2610 $objectsrc->update_price(1, 'auto', 1);
2611 }
2612
2613 print "\n<!-- " . $classname . " info -->";
2614 print "\n";
2615 print '<input type="hidden" name="amount" value="' . $objectsrc->total_ht . '">' . "\n";
2616 print '<input type="hidden" name="total" value="' . $objectsrc->total_ttc . '">' . "\n";
2617 print '<input type="hidden" name="tva" value="' . $objectsrc->total_tva . '">' . "\n";
2618 print '<input type="hidden" name="origin" value="' . $objectsrc->element . '">';
2619 print '<input type="hidden" name="originid" value="' . $objectsrc->id . '">';
2620
2621 $newclassname = $classname;
2622 if ($newclassname == 'Propal') {
2623 $newclassname = 'CommercialProposal';
2624 } elseif ($newclassname == 'Commande') {
2625 $newclassname = 'Order';
2626 } elseif ($newclassname == 'Expedition') {
2627 $newclassname = 'Sending';
2628 } elseif ($newclassname == 'Fichinter') {
2629 $newclassname = 'Intervention';
2630 }
2631
2632 print '<tr><td>' . $langs->trans($newclassname) . '</td><td>' . $objectsrc->getNomUrl(1) . '</td></tr>';
2633 print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td></tr>';
2634 print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2635 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
2636 print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2637 }
2638
2639 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
2640 print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2641 }
2642 print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2643
2644 if (isModEnabled("multicurrency")) {
2645 print '<tr><td>' . $langs->trans('MulticurrencyAmountHT') . '</td><td>' . price($objectsrc->multicurrency_total_ht) . '</td></tr>';
2646 print '<tr><td>' . $langs->trans('MulticurrencyAmountVAT') . '</td><td>' . price($objectsrc->multicurrency_total_tva) . "</td></tr>";
2647 print '<tr><td>' . $langs->trans('MulticurrencyAmountTTC') . '</td><td>' . price($objectsrc->multicurrency_total_ttc) . "</td></tr>";
2648 }
2649 }
2650
2651 print "</table>\n";
2652
2653
2654 /*
2655 * Combobox for copy function
2656 */
2657
2658 if (!getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2659 print '<input type="hidden" name="createmode" value="empty">';
2660 }
2661
2662 if (getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2663 print '<br><table>';
2664
2665 // For backward compatibility
2666 print '<tr>';
2667 print '<td><input type="radio" name="createmode" value="copy"></td>';
2668 print '<td>' . $langs->trans("CopyPropalFrom") . ' </td>';
2669 print '<td>';
2670 $liste_propal = array();
2671 $liste_propal[0] = '';
2672
2673 $sql = "SELECT p.rowid as id, p.ref, s.nom";
2674 $sql .= " FROM " . MAIN_DB_PREFIX . "propal p";
2675 $sql .= ", " . MAIN_DB_PREFIX . "societe s";
2676 $sql .= " WHERE s.rowid = p.fk_soc";
2677 $sql .= " AND p.entity IN (" . getEntity('propal') . ")";
2678 $sql .= " AND p.fk_statut <> 0";
2679 $sql .= " ORDER BY Id";
2680
2681 $resql = $db->query($sql);
2682 if ($resql) {
2683 $num = $db->num_rows($resql);
2684 $i = 0;
2685 while ($i < $num) {
2686 $row = $db->fetch_row($resql);
2687 $propalRefAndSocName = $row[1] . " - " . $row[2];
2688 $liste_propal[$row[0]] = $propalRefAndSocName;
2689 $i++;
2690 }
2691 print $form->selectarray("copie_propal", $liste_propal, 0);
2692 } else {
2693 dol_print_error($db);
2694 }
2695 print '</td></tr>';
2696
2697 print '<tr><td class="tdtop"><input type="radio" name="createmode" value="empty" checked></td>';
2698 print '<td valign="top" colspan="2">' . $langs->trans("CreateEmptyPropal") . '</td></tr>';
2699 print '</table>';
2700 }
2701 }
2702
2703 print dol_get_fiche_end();
2704
2705 $langs->load("bills");
2706
2707 print $form->buttonsSaveCancel("CreateDraft");
2708
2709 print "</form>";
2710
2711
2712 // Show origin lines
2713 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2714 print '<br>';
2715
2716 $title = $langs->trans('ProductsAndServices');
2717 print load_fiche_titre($title);
2718
2719 print '<div class="div-table-responsive-no-min">';
2720 print '<table class="noborder centpercent">';
2721
2722 $objectsrc->printOriginLinesList();
2723
2724 print '</table>';
2725 print '</div>';
2726 }
2727} elseif ($object->id > 0) {
2728 /*
2729 * Show object in view mode
2730 */
2731 $object->fetch_thirdparty();
2732 if ($object->thirdparty) {
2733 $soc = $object->thirdparty;
2734 } else {
2735 $soc = new Societe($db);
2736 }
2737
2738 $head = propal_prepare_head($object);
2739 print dol_get_fiche_head($head, 'comm', $langs->trans('Proposal'), -1, $object->picto, 0, '', '', 0, '', 1);
2740
2741 $formconfirm = '';
2742
2743 // Clone confirmation
2744 if ($action == 'clone') {
2745 // Create an array for form
2746 $filter = '(s.client:IN:1,2,3)';
2747 $formquestion = array(
2748 // 'text' => $langs->trans("ConfirmClone"),
2749 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
2750 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOSTINT('socid'), 'socid', $filter, '', 0, 0, array(), 0, 'maxwidth300')),
2751 array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => 0),
2752 array('type' => 'checkbox', 'name' => 'update_desc', 'label' => $langs->trans('PuttingDescUpToDate'), 'value' => 0),
2753 );
2754 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY') && !empty($object->delivery_date)) {
2755 $formquestion[] = array('type' => 'date', 'name' => 'date_delivery', 'label' => $langs->trans("DeliveryDate"), 'value' => $object->delivery_date);
2756 }
2757 // Incomplete payment. We ask if reason = discount or other
2758 $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);
2759 }
2760
2761 // Subtotal line form
2762 if ($action == 'add_title_line') {
2763 $langs->load('subtotals');
2764 $type = 'title';
2765 $depth_array = $object->getPossibleLevels($langs);
2766 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2767 } elseif ($action == 'add_subtotal_line') {
2768 $langs->load('subtotals');
2769 $type = 'subtotal';
2770 $titles = $object->getPossibleTitles();
2771 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2772 }
2773
2774 if ($action == 'closeas') {
2775 //Form to close proposal (signed or not)
2776 $formquestion = array();
2777 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2778 $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)));
2779 }
2780 $formquestion[] = array('type' => 'text', 'name' => 'note_private', 'label' => $langs->trans("Note"), 'value' => ''); // Field to complete private note (not replace)
2781
2782 if (getDolGlobalInt('PROPOSAL_SUGGEST_DOWN_PAYMENT_INVOICE_CREATION')) {
2783 // This is a hidden option:
2784 // Suggestion to create invoice during proposal signature is not enabled by default.
2785 // Such choice should be managed by the workflow module and trigger. This option generates conflicts with some setup.
2786 // It may also break step of creating an order when invoicing must be done from orders and not from proposal
2787 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
2788
2789 if (!empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
2790 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
2791
2792 $object->fetchObjectLinked();
2793
2794 $eligibleForDepositGeneration = true;
2795
2796 if (array_key_exists('facture', $object->linkedObjects)) {
2797 foreach ($object->linkedObjects['facture'] as $invoice) {
2798 '@phan-var-force Facture $invoice';
2800 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2801 $eligibleForDepositGeneration = false;
2802 break;
2803 }
2804 }
2805 }
2806
2807 if ($eligibleForDepositGeneration && array_key_exists('commande', $object->linkedObjects)) {
2808 foreach ($object->linkedObjects['commande'] as $order) {
2809 $order->fetchObjectLinked();
2810
2811 if (array_key_exists('facture', $order->linkedObjects)) {
2812 foreach ($order->linkedObjects['facture'] as $invoice) {
2813 '@phan-var-force Facture $invoice';
2815 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2816 $eligibleForDepositGeneration = false;
2817 break 2;
2818 }
2819 }
2820 }
2821 }
2822 }
2823
2824
2825 if ($eligibleForDepositGeneration) {
2826 $formquestion[] = array(
2827 'type' => 'checkbox',
2828 'tdclass' => 'showonlyifsigned',
2829 'name' => 'generate_deposit',
2830 'morecss' => 'margintoponly marginbottomonly',
2831 'label' => $form->textwithpicto($langs->trans('GenerateDeposit', $object->deposit_percent), $langs->trans('DepositGenerationPermittedByThePaymentTermsSelected'))
2832 );
2833
2834 $formquestion[] = array(
2835 'type' => 'date',
2836 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2837 'name' => 'datef',
2838 'label' => $langs->trans('DateInvoice'),
2839 'value' => dol_now(),
2840 'datenow' => true
2841 );
2842
2843 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2844 $formquestion[] = array(
2845 'type' => 'date',
2846 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2847 'name' => 'date_pointoftax',
2848 'label' => $langs->trans('DatePointOfTax'),
2849 'value' => dol_now(),
2850 'datenow' => true
2851 );
2852 }
2853
2854 $paymentTermsSelect = $form->getSelectConditionsPaiements(0, 'cond_reglement_id', -1, 0, 1, 'minwidth200');
2855
2856 $formquestion[] = array(
2857 'type' => 'other',
2858 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2859 'name' => 'cond_reglement_id',
2860 'label' => $langs->trans('PaymentTerm'),
2861 'value' => $paymentTermsSelect
2862 );
2863
2864 $formquestion[] = array(
2865 'type' => 'checkbox',
2866 'tdclass' => 'showonlyifgeneratedeposit',
2867 'name' => 'validate_generated_deposit',
2868 'morecss' => 'margintoponly marginbottomonly',
2869 'label' => $langs->trans('ValidateGeneratedDeposit')
2870 );
2871
2872 $formquestion[] = array(
2873 'type' => 'onecolumn',
2874 'value' => '
2875 <script>
2876 let signedValue = ' . $object::STATUS_SIGNED . ';
2877
2878 $(document).ready(function() {
2879 $("[name=generate_deposit]").change(function () {
2880 let $self = $(this);
2881 let $target = $(".showonlyifgeneratedeposit").parent(".tagtr");
2882
2883 if (! $self.parents(".tagtr").is(":hidden") && $self.is(":checked")) {
2884 $target.show();
2885 } else {
2886 $target.hide();
2887 }
2888
2889 return true;
2890 });
2891
2892 $("#statut").change(function() {
2893 let $target = $(".showonlyifsigned").parent(".tagtr");
2894
2895 if ($(this).val() == signedValue) {
2896 $target.show();
2897 } else {
2898 $target.hide();
2899 }
2900
2901 $("[name=generate_deposit]").trigger("change");
2902
2903 return true;
2904 });
2905
2906 $("#statut").trigger("change");
2907 });
2908 </script>
2909 '
2910 );
2911 }
2912 }
2913 }
2914
2915 if (isModEnabled('notification')) {
2916 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2917 $notify = new Notify($db);
2918 $formquestion = array_merge($formquestion, array(
2919 array('type' => 'onecolumn', 'value' => $notify->confirmMessage('PROPAL_CLOSE_SIGNED', $object->socid, $object)),
2920 ));
2921 }
2922
2923 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2924 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SetAcceptedRefused'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2925 } else {
2926 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?statut=3&id=' . $object->id, $langs->trans('Close'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2927 }
2928 } elseif ($action == 'cancel') {
2929 // Confirm cancel
2930 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans("CancelPropal"), $langs->trans('ConfirmCancelPropal', $object->ref), 'confirm_cancel', '', 0, 1);
2931 } elseif ($action == 'delete') {
2932 // Confirm delete
2933 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('DeleteProp'), $langs->trans('ConfirmDeleteProp', $object->ref), 'confirm_delete', '', 0, 1);
2934 } elseif ($action == 'reopen') {
2935 // Confirm reopen
2936 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ReOpen'), $langs->trans('ConfirmReOpenProp', $object->ref), 'confirm_reopen', '', 0, 1);
2937 } elseif ($action == 'ask_deleteline') {
2938 // Confirmation delete product/service line
2939 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1);
2940 } elseif ($action == 'ask_subtotal_deleteline') {
2941 // Confirmation de la suppression d'une ligne subtotal
2942 $langs->load("subtotals");
2943 $title = "DeleteSubtotalLine";
2944 $question = "ConfirmDeleteSubtotalLine";
2945 if (GETPOST('type') == 'title') {
2946 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2947 $title = "DeleteTitleLine";
2948 $question = "ConfirmDeleteTitleLine";
2949 }
2950 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2951 } elseif ($action == 'validate') {
2952 // Confirm validate proposal
2953 $error = 0;
2954
2955 // We verify whether the object is provisionally numbering
2956 $ref = substr($object->ref, 1, 4);
2957 if ($ref == 'PROV' || $ref == '') {
2958 $numref = $object->getNextNumRef($soc);
2959 if (empty($numref)) {
2960 $error++;
2961 setEventMessages($object->error, $object->errors, 'errors');
2962 }
2963 } else {
2964 $numref = (string) $object->ref;
2965 }
2966
2967 $text = $langs->trans('ConfirmValidateProp', $numref);
2968 if (isModEnabled('notification')) {
2969 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2970 $notify = new Notify($db);
2971 $text .= '<br>';
2972 $text .= $notify->confirmMessage('PROPAL_VALIDATE', $object->socid, $object);
2973 }
2974
2975 // mandatoryPeriod
2976 $nbMandated = 0;
2977 foreach ($object->lines as $line) {
2978 $res = $line->fetch_product();
2979 if ($res > 0) {
2980 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
2981 $nbMandated++;
2982 break;
2983 }
2984 }
2985 }
2986 if ($nbMandated > 0) {
2987 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
2988 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
2989 $error++;
2990 } else {
2991 $text .= '<div><span class="clearboth nowraponall warning">' . img_warning() . $langs->trans("mandatoryPeriodNeedTobeSetMsgValidate") . '</span></div>';
2992 }
2993 }
2994
2995 if (!$error) {
2996 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ValidateProp'), $text, 'confirm_validate', '', 0, 1, 240);
2997 }
2998 }
2999
3000 // Call Hook formConfirm
3001 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
3002 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3003 if (empty($reshook)) {
3004 $formconfirm .= $hookmanager->resPrint;
3005 } elseif ($reshook > 0) {
3006 $formconfirm = $hookmanager->resPrint;
3007 }
3008
3009 // Print form confirm
3010 print $formconfirm;
3011
3012
3013 // Proposal card
3014
3015 $linkback = '<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
3016
3017 $morehtmlref = '<div class="refidno">';
3018 // Ref customer
3019 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string', '', 0, 1);
3020 $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);
3021 // Thirdparty
3022 $morehtmlref .= '<br>' . $soc->getNomUrl(1, 'customer');
3023 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $soc->id > 0) {
3024 $morehtmlref .= ' (<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?socid=' . ((int) $soc->id) . '">' . $langs->trans("OtherProposals") . '</a>)';
3025 }
3026 // Project
3027 if (isModEnabled('project')) {
3028 $langs->load("projects");
3029 $morehtmlref .= '<br>';
3030 if ($usercancreate) {
3031 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
3032 if ($action != 'classify') {
3033 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
3034 }
3035 $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');
3036 } else {
3037 if (!empty($object->fk_project)) {
3038 $proj = new Project($db);
3039 $proj->fetch($object->fk_project);
3040 $morehtmlref .= $proj->getNomUrl(1);
3041 if ($proj->title) {
3042 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
3043 }
3044 }
3045 }
3046 }
3047 $morehtmlref .= '</div>';
3048
3049
3050 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
3051
3052 // Call Hook tabContentViewProposal
3053 $parameters = array();
3054 // Note that $action and $object may be modified by hook
3055 $reshook = $hookmanager->executeHooks('tabContentViewProposal', $parameters, $object, $action);
3056 if (empty($reshook)) {
3057 print '<div class="fichecenter">';
3058 print '<div class="fichehalfleft">';
3059 print '<div class="underbanner clearboth"></div>';
3060
3061 print '<table class="border tableforfield centpercent">';
3062
3063 // Link for thirdparty discounts
3064 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
3065 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3066 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3067 } else {
3068 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
3069 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
3070 }
3071
3072 print '<tr><td class="titlefieldmax45">' . $langs->trans('Discounts') . '</td><td>';
3073
3074 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
3075 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
3076 $absolute_discount = price2num($absolute_discount, 'MT');
3077 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
3078
3079 $caneditfield = ($object->status != Propal::STATUS_SIGNED && $object->status != Propal::STATUS_BILLED);
3080
3081 $thirdparty = $soc;
3082 $discount_type = 0;
3083 $backtopage = $_SERVER["PHP_SELF"] . '?id=' . $object->id;
3084 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
3085
3086 print '</td></tr>';
3087
3088 // Date of proposal
3089 print '<tr>';
3090 print '<td>';
3091 // print '<table class="nobordernopadding" width="100%"><tr><td>';
3092 // print $langs->trans('DatePropal');
3093 // print '</td>';
3094 // if ($action != 'editdate' && $usercancreate && $caneditfield) {
3095 // 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>';
3096 // }
3097
3098 // print '</tr></table>';
3099 $editenable = $usercancreate && $caneditfield && $object->status == Propal::STATUS_DRAFT;
3100 print $form->editfieldkey("DatePropal", 'date', '', $object, (int) $editenable);
3101 print '</td><td class="valuefield">';
3102 if ($action == 'editdate' && $usercancreate && $caneditfield) {
3103 print '<form name="editdate" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3104 print '<input type="hidden" name="token" value="' . newToken() . '">';
3105 print '<input type="hidden" name="action" value="setdate">';
3106 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3107 print $form->selectDate($object->date, 're', 0, 0, 0, "editdate");
3108 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3109 print '</form>';
3110 } else {
3111 if ($object->date) {
3112 print dol_print_date($object->date, 'day');
3113 } else {
3114 print '&nbsp;';
3115 }
3116 }
3117 print '</td>';
3118
3119 // Date end proposal
3120 print '<tr>';
3121 print '<td>';
3122 print '<table class="nobordernopadding centpercent"><tr><td>';
3123 print $langs->trans('DateEndPropal');
3124 print '</td>';
3125 if ($action != 'editecheance' && $usercancreate && $caneditfield) {
3126 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>';
3127 }
3128 print '</tr></table>';
3129 print '</td><td class="valuefield">';
3130 if ($action == 'editecheance' && $usercancreate && $caneditfield) {
3131 print '<form name="editecheance" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3132 print '<input type="hidden" name="token" value="' . newToken() . '">';
3133 print '<input type="hidden" name="action" value="setecheance">';
3134 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3135 print $form->selectDate($object->fin_validite, 'ech', 0, 0, 0, "editecheance");
3136 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3137 print '</form>';
3138 } else {
3139 if (!empty($object->fin_validite)) {
3140 print dol_print_date($object->fin_validite, 'day');
3141 if ($object->status == Propal::STATUS_VALIDATED && $object->fin_validite < ($now - getWarningDelay('propal', 'cloture'))) {
3142 print img_warning($langs->trans("Late"));
3143 }
3144 } else {
3145 print '&nbsp;';
3146 }
3147 }
3148 print '</td>';
3149 print '</tr>';
3150
3151 // Payment term
3152 print '<tr><td>';
3153 print '<table class="nobordernopadding centpercent"><tr><td>';
3154 print $langs->trans('PaymentConditionsShort');
3155 print '</td>';
3156 if ($action != 'editconditions' && $usercancreate && $caneditfield) {
3157 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>';
3158 }
3159 print '</tr></table>';
3160 print '</td><td class="valuefield">';
3161 if ($action == 'editconditions' && $usercancreate && $caneditfield) {
3162 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'cond_reglement_id', 0, '', 1, $object->deposit_percent);
3163 } else {
3164 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'none', 0, '', 1, $object->deposit_percent);
3165 }
3166 print '</td>';
3167 print '</tr>';
3168
3169 // Payment mode
3170 print '<tr class="field_mode_reglement_id">';
3171 print '<td>';
3172 print '<table class="nobordernopadding centpercent"><tr><td>';
3173 print $langs->trans('PaymentMode');
3174 print '</td>';
3175 if ($action != 'editmode' && $usercancreate && $caneditfield) {
3176 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>';
3177 }
3178 print '</tr></table>';
3179 print '</td><td class="valuefieldcreate">';
3180 if ($action == 'editmode' && $usercancreate && $caneditfield) {
3181 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
3182 } else {
3183 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'none');
3184 }
3185 print '</td></tr>';
3186
3187 // Delivery date
3188 print '<tr><td>';
3189 print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, (int) ($usercancreate && $caneditfield), 'datepicker');
3190 print '</td><td class="valuefieldedit">';
3191 print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, $usercancreate && $caneditfield, 'datepicker');
3192 print '</td>';
3193 print '</tr>';
3194
3195 // Delivery delay
3196 print '<tr class="fielddeliverydelay"><td>';
3197 print '<table class="nobordernopadding centpercent"><tr><td>';
3198 if (isModEnabled('order')) {
3199 print $form->textwithpicto($langs->trans('AvailabilityPeriod'), $langs->trans('AvailabilityPeriod') . ' (' . $langs->trans('AfterOrder') . ')');
3200 } else {
3201 print $langs->trans('AvailabilityPeriod');
3202 }
3203 print '</td>';
3204 if ($action != 'editavailability' && $usercancreate && $caneditfield) {
3205 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>';
3206 }
3207 print '</tr></table>';
3208 print '</td><td class="valuefield">';
3209 if ($action == 'editavailability' && $usercancreate && $caneditfield) {
3210 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'availability_id', 1);
3211 } else {
3212 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'none', 1);
3213 }
3214
3215 print '</td>';
3216 print '</tr>';
3217
3218 // Shipping Method
3219 if (isModEnabled("shipping")) {
3220 print '<tr><td>';
3221 print '<table class="nobordernopadding centpercent"><tr><td>';
3222 print $langs->trans('SendingMethod');
3223 print '</td>';
3224 if ($action != 'editshippingmethod' && $usercancreate && $caneditfield) {
3225 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>';
3226 }
3227 print '</tr></table>';
3228 print '</td><td class="valuefield">';
3229 if ($action == 'editshippingmethod' && $usercancreate && $caneditfield) {
3230 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'shipping_method_id', 1);
3231 } else {
3232 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'none');
3233 }
3234 print '</td>';
3235 print '</tr>';
3236 }
3237
3238 // Warehouse
3239 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
3240 $langs->load('stocks');
3241 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
3242 $formproduct = new FormProduct($db);
3243 print '<tr class="field_warehouse_id"><td>';
3244 $editenable = $usercancreate;
3245 print $form->editfieldkey("Warehouse", 'warehouse', '', $object, $editenable);
3246 print '</td><td class="valuefieldcreate">';
3247 if ($action == 'editwarehouse') {
3248 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'warehouse_id', 1);
3249 } else {
3250 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'none');
3251 }
3252 print '</td>';
3253 print '</tr>';
3254 }
3255
3256 // Origin of demand
3257 print '<tr><td>';
3258 print '<table class="nobordernopadding centpercent"><tr><td>';
3259 print $langs->trans('Source');
3260 print '</td>';
3261 if ($action != 'editdemandreason' && $usercancreate) {
3262 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>';
3263 }
3264 print '</tr></table>';
3265 print '</td><td class="valuefield">';
3266 if ($action == 'editdemandreason' && $usercancreate) {
3267 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'demand_reason_id', 1);
3268 } else {
3269 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'none');
3270 }
3271 print '</td>';
3272 print '</tr>';
3273
3274 if ($soc->outstanding_limit) {
3275 // Outstanding Bill
3276 print '<tr><td>';
3277 print $langs->trans('OutstandingBill');
3278 print '</td><td class="valuefield">';
3279 $arrayoutstandingbills = $soc->getOutstandingBills();
3280 print($arrayoutstandingbills['opened'] > $soc->outstanding_limit ? img_warning() : '');
3281 print price($arrayoutstandingbills['opened']) . ' / ';
3282 print price($soc->outstanding_limit, 0, $langs, 1, -1, -1, $conf->currency);
3283 print '</td>';
3284 print '</tr>';
3285 }
3286
3287 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
3288 // Bank Account
3289 print '<tr><td>';
3290 print '<table width="100%" class="nobordernopadding"><tr><td>';
3291 print $langs->trans('BankAccount');
3292 print '</td>';
3293 if ($action != 'editbankaccount' && $usercancreate) {
3294 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>';
3295 }
3296 print '</tr></table>';
3297 print '</td><td class="valuefield">';
3298 if ($action == 'editbankaccount') {
3299 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'fk_account', 1);
3300 } else {
3301 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'none');
3302 }
3303 print '</td>';
3304 print '</tr>';
3305 }
3306
3307 if (!getDolGlobalString('PROPOSAL_HIDE_CALCULATED_WEIGHT_VOLUME')) {
3308 $tmparray = $object->getTotalWeightVolume();
3309 $totalWeight = isset($tmparray['weight']) ? $tmparray['weight'] : 0;
3310 $totalVolume = isset($tmparray['volume']) ? $tmparray['volume'] : 0;
3311 if ($totalWeight) {
3312 print '<tr><td>' . $langs->trans("CalculatedWeight") . '</td>';
3313 print '<td class="valuefield">';
3314 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'), 1);
3315 print '</td></tr>';
3316 }
3317 if ($totalVolume) {
3318 print '<tr><td>' . $langs->trans("CalculatedVolume") . '</td>';
3319 print '<td class="valuefield">';
3320 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'), 1);
3321 print '</td></tr>';
3322 }
3323 }
3324
3325 // Incoterms
3326 if (isModEnabled('incoterm')) {
3327 print '<tr><td>';
3328 print '<table width="100%" class="nobordernopadding"><tr><td>';
3329 print $langs->trans('IncotermLabel');
3330 print '<td><td class="right">';
3331 if ($action != 'editincoterm' && $usercancreate && $caneditfield) {
3332 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3333 } else {
3334 print '&nbsp;';
3335 }
3336 print '</td></tr></table>';
3337 print '</td>';
3338 print '<td class="valuefield">';
3339 if ($action == 'editincoterm' && $usercancreate && $caneditfield) {
3340 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3341 } else {
3342 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3343 }
3344 print '</td></tr>';
3345 }
3346
3347 // Categories
3348 if (isModEnabled('category')) {
3349 print '<tr><td>';
3350 print '<table class="nobordernopadding centpercent"><tr><td>';
3351 print $langs->trans("Categories");
3352 print '<td><td class="right">';
3353 if ($usercancreate) {
3354 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/comm/propal/card.php?id='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
3355 } else {
3356 print '&nbsp;';
3357 }
3358 print '</td></tr></table>';
3359 print '</td>';
3360 print '<td>';
3361 if ($action == 'edittags') {
3362 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
3363 print '<input type="hidden" name="action" value="settags">';
3364 print '<input type="hidden" name="token" value="'.newToken().'">';
3365 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
3366 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
3367 print '</form>';
3368 } else {
3369 print $form->showCategories($object->id, Categorie::TYPE_PROPOSAL, 1);
3370 }
3371 print "</td></tr>";
3372 }
3373
3374 // Other attributes
3375 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3376
3377 print '</table>';
3378
3379 print '</div>';
3380 print '<div class="fichehalfright">';
3381 print '<div class="underbanner clearboth"></div>';
3382
3383 print '<table class="border tableforfield centpercent">';
3384
3385 include DOL_DOCUMENT_ROOT . '/core/tpl/object_currency_amount.tpl.php';
3386
3387 print '<tr>';
3388 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
3389 print '<td class="nowrap amountcard right">' . price($object->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3390 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3391 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ht, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3392 }
3393 print '</tr>';
3394
3395 print '<tr>';
3396 print '<td>' . $langs->trans('AmountVAT') . '</td>';
3397 print '<td class="nowrap amountcard right">' . price($object->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3398 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3399 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_tva, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3400 }
3401 print '</tr>';
3402
3403 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
3404 print '<tr>';
3405 print '<td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
3406 print '<td class="nowrap amountcard right">' . price($object->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3407 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3408 $object->multicurrency_total_localtax1 = price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
3409
3410 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax1, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3411 }
3412 print '</tr>';
3413 }
3414
3415 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
3416 print '<tr>';
3417 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
3418 print '<td class="nowrap amountcard right">' . price($object->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3419 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3420 $object->multicurrency_total_localtax2 = price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
3421
3422 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax2, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3423 }
3424 print '</tr>';
3425 }
3426
3427 print '<tr>';
3428 print '<td>' . $langs->trans('AmountTTC') . '</td>';
3429 print '<td class="nowrap amountcard right">' . price($object->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3430 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3431 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3432 }
3433 print '</tr>';
3434
3435 print '</table>';
3436
3437 // Margin Infos
3438 if (isModEnabled('margin')) {
3439 $formmargin->displayMarginInfos($object);
3440 }
3441
3442 print '</div>';
3443 print '</div>';
3444
3445 print '<div class="clearboth"></div><br>';
3446
3447 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3448 $blocname = 'contacts';
3449 $title = $langs->trans('ContactsAddresses');
3450 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3451 }
3452
3453 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
3454 $blocname = 'notes';
3455 $title = $langs->trans('Notes');
3456 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3457 }
3458
3459 /*
3460 * Lines
3461 */
3462
3463 // Get object lines
3464 $result = $object->getLinesArray();
3465
3466 // Add products/services form
3467 //$forceall = 1;
3468 global $inputalsopricewithtax;
3469 $inputalsopricewithtax = 1;
3470
3471 print ' <form name="addproduct" id="addproduct" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
3472 <input type="hidden" name="token" value="' . newToken() . '">
3473 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
3474 <input type="hidden" name="mode" value="">
3475 <input type="hidden" name="page_y" value="">
3476 <input type="hidden" name="backtopage" value="' . $backtopage . '">
3477 <input type="hidden" name="id" value="' . $object->id . '">
3478 ';
3479
3480 if (!empty($conf->use_javascript_ajax) && $object->status == Propal::STATUS_DRAFT) {
3481 if (isModEnabled('subtotals')) {
3482 include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php';
3483 } else {
3484 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3485 }
3486 }
3487
3488 print '<div class="div-table-responsive-no-min">';
3489 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3490 print '<table id="tablelines" class="noborder noshadow centpercent">';
3491 }
3492
3493 if (!empty($object->lines)) {
3494 $object->printObjectLines($action, $mysoc, $object->thirdparty, $lineid, 1);
3495 }
3496
3497 // Form to add new line
3498 if ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines') {
3499 if ($action != 'editline') {
3500 $parameters = array();
3501 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3502 if ($reshook < 0) {
3503 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3504 }
3505 if (empty($reshook)) {
3506 $object->formAddObjectLine(1, $mysoc, $soc);
3507 }
3508 } else {
3509 $parameters = array();
3510 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3511 }
3512 }
3513
3514 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3515 print '</table>';
3516 }
3517 print '</div>';
3518
3519 print "</form>\n";
3520 }
3521
3522 print dol_get_fiche_end();
3523
3524
3525 /*
3526 * Button Actions
3527 */
3528
3529 if ($action != 'presend') {
3530 $numlines = count($object->lines);
3531 print '<div class="tabsAction">';
3532
3533 $parameters = array();
3534 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3535 // modified by hook
3536 if (empty($reshook)) {
3537 if ($action != 'editline') {
3538 // Subtotal
3539 if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals')
3540 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
3541 $langs->load('subtotals');
3542
3543 $url_button = array();
3544
3545 $url_button[] = array(
3546 'lang' => 'subtotals',
3547 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
3548 'perm' => (bool) $usercancreate,
3549 'label' => $langs->trans('AddTitleLine'),
3550 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_title_line&token=' . newToken()
3551 );
3552
3553 $url_button[] = array(
3554 'lang' => 'subtotals',
3555 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
3556 'perm' => (bool) $usercancreate,
3557 'label' => $langs->trans('AddSubtotalLine'),
3558 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_subtotal_line&token=' . newToken()
3559 );
3560
3561 print dolGetButtonAction('', $langs->trans('SubTotal'), 'default', $url_button, '', true);
3562 }
3563
3564 // Validate
3565 if (($object->status == Propal::STATUS_DRAFT && $object->total_ttc >= 0 && count($object->lines) > 0)
3566 || ($object->status == Propal::STATUS_DRAFT && getDolGlobalString('PROPAL_ENABLE_NEGATIVE') && count($object->lines) > 0)
3567 ) {
3568 if ($usercanvalidate) {
3569 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>';
3570 } else {
3571 print '<a class="butActionRefused classfortooltip" href="#">' . $langs->trans('Validate') . '</a>';
3572 }
3573 }
3574 // Create event
3575 /*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.
3576 {
3577 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>';
3578 }*/
3579 // Edit
3580 if ($object->status == Propal::STATUS_VALIDATED && $usercancreate) {
3581 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=modif&token=' . newToken() . '">' . $langs->trans('Modify') . '</a>';
3582 }
3583
3584 // ReOpen
3585 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)))) {
3586 if ($usercanreopen) {
3587 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=reopen&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#reopen') . '"';
3588 print '>' . $langs->trans('ReOpen') . '</a>';
3589 } else {
3590 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ReOpen") . '</a>';
3591 }
3592 }
3593
3594 // Send
3595 if (empty($user->socid)) {
3596 if ($object->status == Propal::STATUS_VALIDATED || $object->status == Propal::STATUS_SIGNED || getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
3597 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '', $usercansend);
3598 }
3599 }
3600
3601 $arrayforbutaction = array();
3602
3603 // Create a sale order
3604 $arrayforbutaction[] = array(
3605 'lang' => 'orders',
3606 'enabled' => (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED),
3607 'perm' => $usercancreateorder,
3608 'label' => 'AddOrder',
3609 'url' => '/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3610 );
3611 /*if (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED) {
3612 if ($usercancreateorder) {
3613 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>';
3614 }
3615 }*/
3616
3617 // Create a purchase order
3618 if (getDolGlobalString('WORKFLOW_CAN_CREATE_PURCHASE_ORDER_FROM_PROPOSAL')) {
3619 $arrayforbutaction[] = array(
3620 'lang' => 'orders',
3621 'enabled' => ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")),
3622 'perm' => $usercancreatepurchaseorder,
3623 'label' => 'AddPurchaseOrder',
3624 'url' => '/fourn/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3625 );
3626 /*if ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")) {
3627 if ($usercancreatepurchaseorder) {
3628 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>';
3629 }
3630 }*/
3631 }
3632
3633 // Create an intervention
3634 $arrayforbutaction[] = array(
3635 'lang' => 'interventions',
3636 'enabled' => (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED),
3637 'perm' => $usercancreateintervention,
3638 'label' => 'AddIntervention',
3639 'url' => '/fichinter/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3640 );
3641 /*if (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED) {
3642 if ($usercancreateintervention) {
3643 $langs->load("interventions");
3644 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>';
3645 }
3646 }*/
3647
3648 // Create contract
3649 $arrayforbutaction[] = array(
3650 'lang' => 'contracts',
3651 'enabled' => (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED),
3652 'perm' => $usercancreatecontract,
3653 'label' => 'AddContract',
3654 'url' => '/contrat/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3655 );
3656 /*if (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED) {
3657 $langs->load("contracts");
3658
3659 if ($usercancreatecontract) {
3660 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>';
3661 }
3662 }*/
3663
3664 // Create an invoice and classify billed
3665 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3666 $arrayforbutaction[] = [
3667 'lang' => 'invoice',
3668 'enabled' => isModEnabled('invoice'),
3669 'perm' => $usercancreateinvoice,
3670 'label' => 'CreateBill',
3671 'url' => '/compta/facture/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid),
3672 ];
3673 /*if (isModEnabled('invoice') && $usercancreateinvoice) {
3674 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>';
3675 }*/
3676 }
3677
3678 $actionButtonsParameters = [
3679 "areDropdownButtons" => !getDolGlobalInt("MAIN_REMOVE_DROPDOWN_CREATE_BUTTONS_ON_ORDER"),
3680 "backtopage" => $_SERVER["PHP_SELF"] . "?id=" . ((int) $id)
3681 ];
3682
3683 if ($numlines > 0) {
3684 print dolGetButtonAction('', $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 1, $actionButtonsParameters);
3685 } else {
3686 print dolGetButtonAction($langs->trans("ErrorObjectMustHaveLinesToBeValidated", $object->ref), $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 0, $actionButtonsParameters);
3687 }
3688
3689 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3690 $arrayofinvoiceforpropal = $object->getInvoiceArrayList();
3691 if ((is_array($arrayofinvoiceforpropal) && count($arrayofinvoiceforpropal) > 0) || !getDolGlobalString('WORKFLOW_PROPAL_NEED_INVOICE_TO_BE_CLASSIFIED_BILLED')) {
3692 if ($usercanclose) {
3693 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=classifybilled&token=' . newToken() . '&socid=' . $object->socid . '">' . $langs->trans("ClassifyBilled") . '</a>';
3694 } else {
3695 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ClassifyBilled") . '</a>';
3696 }
3697 }
3698 }
3699
3700 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
3701 // Close as accepted/refused
3702 if ($object->status == Propal::STATUS_VALIDATED) {
3703 if ($usercanclose) {
3704 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3705 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3706 } else {
3707 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '"';
3708 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3709 }
3710 }
3711 } else {
3712 // Set not signed (close)
3713 if ($object->status == Propal::STATUS_DRAFT && $usercanclose) {
3714 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&token=' . newToken() . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3715 print '>' . $langs->trans('SetRefusedAndClose') . '</a>';
3716 }
3717 }
3718
3719 // Cancel propal
3720 if ($object->status > Propal::STATUS_DRAFT && $usercanclose) {
3721 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=cancel&token=' . newToken() . '">' . $langs->trans("CancelPropal") . '</a>';
3722 }
3723
3724 // Clone
3725 if ($usercancreate) {
3726 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>';
3727 }
3728
3729 // Delete
3730 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=delete&token=' . newToken(), 'delete', $usercandelete);
3731 }
3732 }
3733
3734 print '</div>';
3735 }
3736
3737 //Select mail models is same action as presend
3738 if (GETPOST('modelselected')) {
3739 $action = 'presend';
3740 }
3741
3742 if ($action != 'presend') {
3743 print '<div class="fichecenter"><div class="fichehalfleft">';
3744 print '<a name="builddoc"></a>'; // ancre
3745 /*
3746 * Generated documents
3747 */
3748 $objref = dol_sanitizeFileName($object->ref);
3749 $filedir = $conf->propal->multidir_output[$object->entity ?? $conf->entity] . "/" . dol_sanitizeFileName($object->ref);
3750 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3751 $genallowed = $usercanread;
3752 $delallowed = $usercancreate;
3753 $tooltipAfterComboOfModels = '';
3754 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_PROPAL')) {
3755 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_PROPAL_TERMSOFSALE'));
3756 }
3757
3758 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);
3759
3760 // Show links to link elements
3761 $tmparray = $form->showLinkToObjectBlock($object, array(), array('propal'), 1);
3762 $linktoelem = $tmparray['linktoelem'];
3763 $htmltoenteralink = $tmparray['htmltoenteralink'];
3764 print $htmltoenteralink;
3765
3766 $compatibleImportElementsList = false;
3767 if ($user->hasRight('propal', 'creer') && $object->status == Propal::STATUS_DRAFT) {
3768 $compatibleImportElementsList = array('commande', 'propal', 'facture', 'subscription'); // import from linked elements
3769 }
3770 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
3771
3772 // Show online signature link
3773 $useonlinesignature = getDolGlobalInt('PROPOSAL_ALLOW_ONLINESIGN');
3774
3775 if ($object->status != Propal::STATUS_DRAFT && $useonlinesignature) {
3776 print '<br><!-- Link to sign -->';
3777 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3778 print showOnlineSignatureUrl('proposal', $object->ref, $object) . '<br>';
3779 }
3780
3781 print '</div><div class="fichehalfright">';
3782
3783 $MAXEVENT = 10;
3784
3785 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/comm/propal/messaging.php?id=' . $object->id);
3786 $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT . '/comm/propal/agenda.php?id=' . $object->id);
3787
3788 // List of actions on element
3789 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3790 $formactions = new FormActions($db);
3791 $somethingshown = $formactions->showactions($object, 'propal', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
3792
3793 print '</div></div>';
3794 }
3795
3796 // Presend form
3797 $modelmail = 'propal_send';
3798 $defaulttopic = 'SendPropalRef';
3799 $diroutput = $conf->propal->multidir_output[$object->entity ?? $conf->entity];
3800 $trackid = 'pro' . $object->id;
3801
3802 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3803}
3804
3805// End of page
3806llxFooter();
3807$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.