1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* Copyright (C) 2002-2004 Rodolphe Quiedeville <[email protected]> |
4
|
|
|
* Copyright (C) 2004 Eric Seigne <[email protected]> |
5
|
|
|
* Copyright (C) 2004-2011 Laurent Destailleur <[email protected]> |
6
|
|
|
* Copyright (C) 2005 Marc Barilley <[email protected]> |
7
|
|
|
* Copyright (C) 2005-2013 Regis Houssin <[email protected]> |
8
|
|
|
* Copyright (C) 2006 Andre Cianfarani <[email protected]> |
9
|
|
|
* Copyright (C) 2008 Raphael Bertrand <[email protected]> |
10
|
|
|
* Copyright (C) 2010-2020 Juanjo Menent <[email protected]> |
11
|
|
|
* Copyright (C) 2010-2022 Philippe Grand <[email protected]> |
12
|
|
|
* Copyright (C) 2012-2014 Christophe Battarel <[email protected]> |
13
|
|
|
* Copyright (C) 2012 Cedric Salvador <[email protected]> |
14
|
|
|
* Copyright (C) 2013 Florian Henry <[email protected]> |
15
|
|
|
* Copyright (C) 2014-2015 Marcos García <[email protected]> |
16
|
|
|
* Copyright (C) 2018 Nicolas ZABOURI <[email protected]> |
17
|
|
|
* Copyright (C) 2018-2024 Frédéric France <[email protected]> |
18
|
|
|
* Copyright (C) 2018 Ferran Marcet <[email protected]> |
19
|
|
|
* Copyright (C) 2022 ATM Consulting <[email protected]> |
20
|
|
|
* Copyright (C) 2022 OpenDSI <[email protected]> |
21
|
|
|
* Copyright (C) 2022 Gauthier VERDOL <[email protected]> |
22
|
|
|
* Copyright (C) 2023 William Mead <[email protected]> |
23
|
|
|
* Copyright (C) 2024 MDW <[email protected]> |
24
|
|
|
* Copyright (C) 2024 Rafael San José <[email protected]> |
25
|
|
|
* |
26
|
|
|
* This program is free software; you can redistribute it and/or modify |
27
|
|
|
* it under the terms of the GNU General Public License as published by |
28
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
29
|
|
|
* (at your option) any later version. |
30
|
|
|
* |
31
|
|
|
* This program is distributed in the hope that it will be useful, |
32
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
33
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
34
|
|
|
* GNU General Public License for more details. |
35
|
|
|
* |
36
|
|
|
* You should have received a copy of the GNU General Public License |
37
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. |
38
|
|
|
*/ |
39
|
|
|
|
40
|
|
|
namespace Dolibarr\Code\Comm\Classes; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* \file htdocs/comm/propal/class/propal.class.php |
44
|
|
|
* \brief File of class to manage proposals |
45
|
|
|
*/ |
46
|
|
|
|
47
|
|
|
use Dolibarr\Code\Core\Classes\DiscountAbsolute; |
48
|
|
|
use Dolibarr\Code\Core\Classes\WorkboardResponse; |
49
|
|
|
use Dolibarr\Code\Core\Traits\CommonIncoterm; |
50
|
|
|
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency; |
51
|
|
|
use Dolibarr\Code\Product\Classes\Product; |
52
|
|
|
use Dolibarr\Code\Product\Classes\ProductCustomerPrice; |
53
|
|
|
use Dolibarr\Code\User\Classes\User; |
54
|
|
|
use Dolibarr\Core\Base\CommonObject; |
55
|
|
|
use DoliDB; |
56
|
|
|
|
57
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/margin/lib/margins.lib.php'; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Class to manage proposals |
61
|
|
|
*/ |
62
|
|
|
class Propal extends CommonObject |
63
|
|
|
{ |
64
|
|
|
use CommonIncoterm; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Canceled status |
68
|
|
|
*/ |
69
|
|
|
const STATUS_CANCELED = -1; |
70
|
|
|
/** |
71
|
|
|
* Draft status |
72
|
|
|
*/ |
73
|
|
|
const STATUS_DRAFT = 0; |
74
|
|
|
/** |
75
|
|
|
* Validated status |
76
|
|
|
*/ |
77
|
|
|
const STATUS_VALIDATED = 1; |
78
|
|
|
/** |
79
|
|
|
* Signed quote |
80
|
|
|
*/ |
81
|
|
|
const STATUS_SIGNED = 2; |
82
|
|
|
/** |
83
|
|
|
* Not signed quote |
84
|
|
|
*/ |
85
|
|
|
const STATUS_NOTSIGNED = 3; |
86
|
|
|
/** |
87
|
|
|
* Billed or processed quote |
88
|
|
|
*/ |
89
|
|
|
const STATUS_BILLED = 4; |
90
|
|
|
/** |
91
|
|
|
* @var string code |
92
|
|
|
*/ |
93
|
|
|
public $code = ""; |
94
|
|
|
/** |
95
|
|
|
* @var string ID to identify managed object |
96
|
|
|
*/ |
97
|
|
|
public $element = 'propal'; |
98
|
|
|
/** |
99
|
|
|
* @var string Name of table without prefix where object is stored |
100
|
|
|
*/ |
101
|
|
|
public $table_element = 'propal'; |
102
|
|
|
/** |
103
|
|
|
* @var string Name of subtable line |
104
|
|
|
*/ |
105
|
|
|
public $table_element_line = 'propaldet'; |
106
|
|
|
/** |
107
|
|
|
* @var string Fieldname with ID of parent key if this field has a parent |
108
|
|
|
*/ |
109
|
|
|
public $fk_element = 'fk_propal'; |
110
|
|
|
/** |
111
|
|
|
* @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png |
112
|
|
|
*/ |
113
|
|
|
public $picto = 'propal'; |
114
|
|
|
/** |
115
|
|
|
* 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user |
116
|
|
|
* @var integer |
117
|
|
|
*/ |
118
|
|
|
public $restrictiononfksoc = 1; |
119
|
|
|
/** |
120
|
|
|
* ID of the client |
121
|
|
|
* @var int |
122
|
|
|
*/ |
123
|
|
|
public $socid; |
124
|
|
|
/** |
125
|
|
|
* ID of the contact |
126
|
|
|
* @var int |
127
|
|
|
*/ |
128
|
|
|
public $contactid; |
129
|
|
|
public $author; |
130
|
|
|
/** |
131
|
|
|
* Ref from thirdparty |
132
|
|
|
* @var string |
133
|
|
|
* @deprecated |
134
|
|
|
* @see $ref_customer |
135
|
|
|
*/ |
136
|
|
|
public $ref_client; |
137
|
|
|
/** |
138
|
|
|
* Ref from thirdparty |
139
|
|
|
* @var string |
140
|
|
|
*/ |
141
|
|
|
public $ref_customer; |
142
|
|
|
/** |
143
|
|
|
* @var static oldcopy with propal properties |
144
|
|
|
*/ |
145
|
|
|
public $oldcopy; |
146
|
|
|
/** |
147
|
|
|
* Status of the quote |
148
|
|
|
* @var int |
149
|
|
|
* @deprecated Try to use $status now |
150
|
|
|
* @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED |
151
|
|
|
*/ |
152
|
|
|
public $statut; |
153
|
|
|
/** |
154
|
|
|
* Status of the quote |
155
|
|
|
* @var int |
156
|
|
|
* @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED |
157
|
|
|
*/ |
158
|
|
|
public $status; |
159
|
|
|
/** |
160
|
|
|
* @deprecated |
161
|
|
|
* @see $date_creation |
162
|
|
|
*/ |
163
|
|
|
public $datec; |
164
|
|
|
/** |
165
|
|
|
* @var integer|string $date_creation ; |
166
|
|
|
*/ |
167
|
|
|
public $date_creation; |
168
|
|
|
/** |
169
|
|
|
* @deprecated |
170
|
|
|
* @see $date_validation |
171
|
|
|
*/ |
172
|
|
|
public $datev; |
173
|
|
|
/** |
174
|
|
|
* @var integer|string $date_validation ; |
175
|
|
|
*/ |
176
|
|
|
public $date_validation; // Date expected of shipment (date starting shipment, not the reception that occurs some days after) |
177
|
|
|
/** |
178
|
|
|
* @var integer|string $date_signature ; |
179
|
|
|
*/ |
180
|
|
|
public $date_signature; |
181
|
|
|
/** |
182
|
|
|
* @var User $user_signature |
183
|
|
|
*/ |
184
|
|
|
public $user_signature; |
185
|
|
|
/** |
186
|
|
|
* @var integer|string date of the quote; |
187
|
|
|
*/ |
188
|
|
|
public $date; |
189
|
|
|
/** |
190
|
|
|
* @deprecated |
191
|
|
|
* @see $date |
192
|
|
|
*/ |
193
|
|
|
public $datep; |
194
|
|
|
/** |
195
|
|
|
* @var integer|string $delivery_date ; |
196
|
|
|
*/ |
197
|
|
|
public $delivery_date; |
198
|
|
|
public $fin_validite; // code |
199
|
|
|
public $user_author_id; // label |
200
|
|
|
/** |
201
|
|
|
* @deprecated |
202
|
|
|
* @see $total_ht |
203
|
|
|
*/ |
204
|
|
|
public $price; // label doc |
205
|
|
|
/** |
206
|
|
|
* @deprecated |
207
|
|
|
* @see $total_tva |
208
|
|
|
*/ |
209
|
|
|
public $tva; // code |
210
|
|
|
/** |
211
|
|
|
* @deprecated |
212
|
|
|
* @see $total_ttc |
213
|
|
|
*/ |
214
|
|
|
public $total; // label |
215
|
|
|
public $cond_reglement_code; |
216
|
|
|
public $cond_reglement; |
217
|
|
|
public $cond_reglement_doc; |
218
|
|
|
public $mode_reglement_code; |
219
|
|
|
public $mode_reglement; |
220
|
|
|
public $deposit_percent; |
221
|
|
|
/** |
222
|
|
|
* @var int ID |
223
|
|
|
* @deprecated |
224
|
|
|
*/ |
225
|
|
|
public $fk_address; |
226
|
|
|
public $address_type; |
227
|
|
|
public $address; |
228
|
|
|
/** |
229
|
|
|
* @var int availability ID |
230
|
|
|
*/ |
231
|
|
|
public $availability_id; // id |
232
|
|
|
/** |
233
|
|
|
* @var int availability ID |
234
|
|
|
* @deprecated |
235
|
|
|
* @see $availability_id |
236
|
|
|
*/ |
237
|
|
|
public $fk_availability; // code |
238
|
|
|
/** |
239
|
|
|
* @var string availability code |
240
|
|
|
*/ |
241
|
|
|
public $availability_code; // label |
242
|
|
|
/** |
243
|
|
|
* @var string availability label |
244
|
|
|
*/ |
245
|
|
|
public $availability; |
246
|
|
|
public $duree_validite; |
247
|
|
|
public $demand_reason_id; |
248
|
|
|
public $demand_reason_code; |
249
|
|
|
public $demand_reason; |
250
|
|
|
public $warehouse_id; |
251
|
|
|
|
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* 'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password') |
255
|
|
|
* Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)" |
256
|
|
|
* 'label' the translation key. |
257
|
|
|
* 'enabled' is a condition when the field must be managed. |
258
|
|
|
* 'position' is the sort order of field. |
259
|
|
|
* 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). |
260
|
|
|
* 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) |
261
|
|
|
* 'noteditable' says if field is not editable (1 or 0) |
262
|
|
|
* 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. |
263
|
|
|
* 'index' if we want an index in database. |
264
|
|
|
* 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...). |
265
|
|
|
* 'searchall' is 1 if we want to search in this field when making a search from the quick search button. |
266
|
|
|
* 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). |
267
|
|
|
* 'css' is the CSS style to use on field. For example: 'maxwidth200' |
268
|
|
|
* 'help' is a string visible as a tooltip on field |
269
|
|
|
* 'showoncombobox' if value of the field must be visible into the label of the combobox that list record |
270
|
|
|
* 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. |
271
|
|
|
* 'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") |
272
|
|
|
* 'comment' is not used. You can store here any text of your choice. It is not used by application. |
273
|
|
|
* |
274
|
|
|
* Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. |
275
|
|
|
*/ |
276
|
|
|
|
277
|
|
|
// BEGIN MODULEBUILDER PROPERTIES |
278
|
|
|
public $extraparams = array(); |
279
|
|
|
// END MODULEBUILDER PROPERTIES |
280
|
|
|
/** |
281
|
|
|
* @var PropaleLigne[] |
282
|
|
|
*/ |
283
|
|
|
public $lines = array(); |
284
|
|
|
/** |
285
|
|
|
* @var PropaleLigne |
286
|
|
|
*/ |
287
|
|
|
public $line; |
288
|
|
|
public $labelStatus = array(); |
289
|
|
|
public $labelStatusShort = array(); |
290
|
|
|
/** |
291
|
|
|
* @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}> Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. |
292
|
|
|
*/ |
293
|
|
|
public $fields = array( |
294
|
|
|
'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10), |
295
|
|
|
'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1), |
296
|
|
|
'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20), |
297
|
|
|
'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22), |
298
|
|
|
'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40), |
299
|
|
|
'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23), |
300
|
|
|
'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Fk projet', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 24), |
301
|
|
|
'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25), |
302
|
|
|
'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55), |
303
|
|
|
'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60), |
304
|
|
|
'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65), |
305
|
|
|
'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70), |
306
|
|
|
'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75), |
307
|
|
|
'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80), |
308
|
|
|
'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85), |
309
|
|
|
'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90), |
310
|
|
|
'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95), |
311
|
|
|
'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105), |
312
|
|
|
'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1), |
313
|
|
|
'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1), |
314
|
|
|
'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1), |
315
|
|
|
'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1), |
316
|
|
|
'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1), |
317
|
|
|
'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150), |
318
|
|
|
'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155), |
319
|
|
|
'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160), |
320
|
|
|
'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161), |
321
|
|
|
'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165), |
322
|
|
|
'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170), |
323
|
|
|
'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175), |
324
|
|
|
'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180), |
325
|
|
|
'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185), |
326
|
|
|
'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190), |
327
|
|
|
'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191), |
328
|
|
|
'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195), |
329
|
|
|
'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated |
330
|
|
|
'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205), |
331
|
|
|
'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215), |
332
|
|
|
'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220), |
333
|
|
|
'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 225), |
334
|
|
|
'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230), |
335
|
|
|
'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235), |
336
|
|
|
'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1), |
337
|
|
|
'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1), |
338
|
|
|
'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1), |
339
|
|
|
'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1), |
340
|
|
|
'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260), |
341
|
|
|
'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500), |
342
|
|
|
'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900), |
343
|
|
|
); |
344
|
|
|
/** |
345
|
|
|
* {@inheritdoc} |
346
|
|
|
*/ |
347
|
|
|
protected $table_ref_field = 'ref'; // Todo rename into STATUS_CLOSE ? |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Constructor |
351
|
|
|
* |
352
|
|
|
* @param DoliDB $db Database handler |
353
|
|
|
* @param int $socid Id third party |
354
|
|
|
* @param int $propalid Id proposal |
355
|
|
|
*/ |
356
|
|
|
public function __construct($db, $socid = 0, $propalid = 0) |
357
|
|
|
{ |
358
|
|
|
$this->db = $db; |
359
|
|
|
|
360
|
|
|
$this->ismultientitymanaged = 1; |
361
|
|
|
$this->socid = $socid; |
362
|
|
|
$this->id = $propalid; |
363
|
|
|
|
364
|
|
|
$this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0); |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
|
368
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Function used to replace a thirdparty id with another one. |
372
|
|
|
* |
373
|
|
|
* @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking coding test |
374
|
|
|
* @param int $origin_id Old thirdparty id |
375
|
|
|
* @param int $dest_id New thirdparty id |
376
|
|
|
* @return bool |
377
|
|
|
*/ |
378
|
|
|
public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id) |
379
|
|
|
{ |
380
|
|
|
$tables = array( |
381
|
|
|
'propal' |
382
|
|
|
); |
383
|
|
|
|
384
|
|
|
return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Function used to replace a product id with another one. |
391
|
|
|
* |
392
|
|
|
* @param DoliDB $db Database handler |
393
|
|
|
* @param int $origin_id Old product id |
394
|
|
|
* @param int $dest_id New product id |
395
|
|
|
* @return bool |
396
|
|
|
*/ |
397
|
|
|
public static function replaceProduct(DoliDB $db, $origin_id, $dest_id) |
398
|
|
|
{ |
399
|
|
|
$tables = array( |
400
|
|
|
'propaldet' |
401
|
|
|
); |
402
|
|
|
|
403
|
|
|
return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Add line into array ->lines |
408
|
|
|
* $this->thirdparty should be loaded |
409
|
|
|
* |
410
|
|
|
* @param int $idproduct Product Id to add |
411
|
|
|
* @param float $qty Quantity |
412
|
|
|
* @param float $remise_percent Discount effected on Product |
413
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
414
|
|
|
* |
415
|
|
|
* TODO Replace calls to this function by generation object Ligne |
416
|
|
|
*/ |
417
|
|
|
public function add_product($idproduct, $qty, $remise_percent = 0) |
418
|
|
|
{ |
419
|
|
|
// phpcs:enable |
420
|
|
|
global $conf, $mysoc; |
421
|
|
|
|
422
|
|
|
if (!$qty) { |
423
|
|
|
$qty = 1; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
dol_syslog(get_only_class($this) . "::add_product $idproduct, $qty, $remise_percent"); |
427
|
|
|
if ($idproduct > 0) { |
428
|
|
|
$prod = new Product($this->db); |
429
|
|
|
$prod->fetch($idproduct); |
430
|
|
|
|
431
|
|
|
$productdesc = $prod->description; |
432
|
|
|
|
433
|
|
|
$tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id); |
434
|
|
|
$tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id); |
435
|
|
|
if (empty($tva_tx)) { |
436
|
|
|
$tva_npr = 0; |
437
|
|
|
} |
438
|
|
|
$vat_src_code = ''; // May be defined into tva_tx |
439
|
|
|
|
440
|
|
|
$localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr); |
441
|
|
|
$localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr); |
442
|
|
|
|
443
|
|
|
// multiprices |
444
|
|
|
if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) { |
445
|
|
|
$price = $prod->multiprices[$this->thirdparty->price_level]; |
446
|
|
|
} else { |
447
|
|
|
$price = $prod->price; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
$line = new PropaleLigne($this->db); |
451
|
|
|
|
452
|
|
|
$line->fk_product = $idproduct; |
453
|
|
|
$line->desc = $productdesc; |
454
|
|
|
$line->qty = $qty; |
455
|
|
|
$line->subprice = $price; |
456
|
|
|
$line->remise_percent = $remise_percent; |
457
|
|
|
$line->vat_src_code = $vat_src_code; |
458
|
|
|
$line->tva_tx = $tva_tx; |
459
|
|
|
$line->fk_unit = $prod->fk_unit; |
460
|
|
|
if ($tva_npr) { |
461
|
|
|
$line->info_bits = 1; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
$this->lines[] = $line; |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
return 1; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Load a proposal from database. Get also lines. |
472
|
|
|
* |
473
|
|
|
* @param int $rowid Id of object to load |
474
|
|
|
* @param string $ref Ref of proposal |
475
|
|
|
* @param string $ref_ext Ref ext of proposal |
476
|
|
|
* @param int $forceentity Entity id to force when searching on ref or ref_ext |
477
|
|
|
* @return int >0 if OK, <0 if KO |
478
|
|
|
*/ |
479
|
|
|
public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0) |
480
|
|
|
{ |
481
|
|
|
$sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc"; |
482
|
|
|
$sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht"; |
483
|
|
|
$sql .= ", p.datec"; |
484
|
|
|
$sql .= ", p.date_signature as dates"; |
485
|
|
|
$sql .= ", p.date_valid as datev"; |
486
|
|
|
$sql .= ", p.datep as dp"; |
487
|
|
|
$sql .= ", p.fin_validite as dfv"; |
488
|
|
|
$sql .= ", p.date_livraison as delivery_date"; |
489
|
|
|
$sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams"; |
490
|
|
|
$sql .= ", p.note_private, p.note_public"; |
491
|
|
|
$sql .= ", p.fk_projet as fk_project, p.fk_statut"; |
492
|
|
|
$sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture"; |
493
|
|
|
$sql .= ", p.fk_delivery_address"; |
494
|
|
|
$sql .= ", p.fk_availability"; |
495
|
|
|
$sql .= ", p.fk_input_reason"; |
496
|
|
|
$sql .= ", p.fk_cond_reglement"; |
497
|
|
|
$sql .= ", p.fk_mode_reglement"; |
498
|
|
|
$sql .= ', p.fk_account'; |
499
|
|
|
$sql .= ", p.fk_shipping_method"; |
500
|
|
|
$sql .= ", p.fk_warehouse"; |
501
|
|
|
$sql .= ", p.fk_incoterms, p.location_incoterms"; |
502
|
|
|
$sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc"; |
503
|
|
|
$sql .= ", p.tms as date_modification"; |
504
|
|
|
$sql .= ", i.libelle as label_incoterms"; |
505
|
|
|
$sql .= ", c.label as statut_label"; |
506
|
|
|
$sql .= ", ca.code as availability_code, ca.label as availability"; |
507
|
|
|
$sql .= ", dr.code as demand_reason_code, dr.label as demand_reason"; |
508
|
|
|
$sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent"; |
509
|
|
|
$sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement"; |
510
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "propal as p"; |
511
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_propalst as c ON p.fk_statut = c.id'; |
512
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN (' . getEntity('c_paiement') . ')'; |
513
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN (' . getEntity('c_payment_term') . ')'; |
514
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_availability as ca ON p.fk_availability = ca.rowid'; |
515
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_input_reason as dr ON p.fk_input_reason = dr.rowid'; |
516
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON p.fk_incoterms = i.rowid'; |
517
|
|
|
|
518
|
|
|
if (!empty($ref)) { |
519
|
|
|
if (!empty($forceentity)) { |
520
|
|
|
$sql .= " WHERE p.entity = " . (int)$forceentity; // Check only the current entity because we may have the same reference in several entities |
521
|
|
|
} else { |
522
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('propal') . ")"; |
523
|
|
|
} |
524
|
|
|
$sql .= " AND p.ref='" . $this->db->escape($ref) . "'"; |
525
|
|
|
} else { |
526
|
|
|
// Don't use entity if you use rowid |
527
|
|
|
$sql .= " WHERE p.rowid = " . ((int)$rowid); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
dol_syslog(get_only_class($this) . "::fetch", LOG_DEBUG); |
531
|
|
|
$resql = $this->db->query($sql); |
532
|
|
|
if ($resql) { |
533
|
|
|
if ($this->db->num_rows($resql)) { |
534
|
|
|
$obj = $this->db->fetch_object($resql); |
535
|
|
|
|
536
|
|
|
$this->id = $obj->rowid; |
537
|
|
|
$this->entity = $obj->entity; |
538
|
|
|
|
539
|
|
|
$this->ref = $obj->ref; |
540
|
|
|
$this->ref_client = $obj->ref_client; |
541
|
|
|
$this->ref_customer = $obj->ref_client; |
542
|
|
|
$this->ref_ext = $obj->ref_ext; |
543
|
|
|
|
544
|
|
|
$this->total = $obj->total_ttc; // TODO deprecated |
545
|
|
|
$this->total_ttc = $obj->total_ttc; |
546
|
|
|
$this->total_ht = $obj->total_ht; |
547
|
|
|
$this->total_tva = $obj->total_tva; |
548
|
|
|
$this->total_localtax1 = $obj->localtax1; |
549
|
|
|
$this->total_localtax2 = $obj->localtax2; |
550
|
|
|
|
551
|
|
|
$this->socid = $obj->fk_soc; |
552
|
|
|
$this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty |
553
|
|
|
|
554
|
|
|
$this->fk_project = $obj->fk_project; |
555
|
|
|
$this->project = null; // Clear if another value was already set by fetch_projet |
556
|
|
|
|
557
|
|
|
$this->model_pdf = $obj->model_pdf; |
558
|
|
|
$this->last_main_doc = $obj->last_main_doc; |
559
|
|
|
$this->note = $obj->note_private; // TODO deprecated |
560
|
|
|
$this->note_private = $obj->note_private; |
561
|
|
|
$this->note_public = $obj->note_public; |
562
|
|
|
|
563
|
|
|
$this->status = (int)$obj->fk_statut; |
564
|
|
|
$this->statut = $this->status; // deprecated |
565
|
|
|
|
566
|
|
|
$this->datec = $this->db->jdate($obj->datec); // TODO deprecated |
567
|
|
|
$this->datev = $this->db->jdate($obj->datev); // TODO deprecated |
568
|
|
|
$this->date_creation = $this->db->jdate($obj->datec); //Creation date |
569
|
|
|
$this->date_validation = $this->db->jdate($obj->datev); //Validation date |
570
|
|
|
$this->date_modification = $this->db->jdate($obj->date_modification); // tms |
571
|
|
|
$this->date_signature = $this->db->jdate($obj->dates); // Signature date |
572
|
|
|
$this->date = $this->db->jdate($obj->dp); // Proposal date |
573
|
|
|
$this->datep = $this->db->jdate($obj->dp); // deprecated |
574
|
|
|
$this->fin_validite = $this->db->jdate($obj->dfv); |
575
|
|
|
$this->delivery_date = $this->db->jdate($obj->delivery_date); |
576
|
|
|
$this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null; |
577
|
|
|
$this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null; |
578
|
|
|
$this->availability_id = $obj->fk_availability; |
579
|
|
|
$this->availability_code = $obj->availability_code; |
580
|
|
|
$this->availability = $obj->availability; |
581
|
|
|
$this->demand_reason_id = $obj->fk_input_reason; |
582
|
|
|
$this->demand_reason_code = $obj->demand_reason_code; |
583
|
|
|
$this->demand_reason = $obj->demand_reason; |
584
|
|
|
$this->fk_address = $obj->fk_delivery_address; |
585
|
|
|
|
586
|
|
|
$this->mode_reglement_id = $obj->fk_mode_reglement; |
587
|
|
|
$this->mode_reglement_code = $obj->mode_reglement_code; |
588
|
|
|
$this->mode_reglement = $obj->mode_reglement; |
589
|
|
|
$this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null; |
590
|
|
|
$this->cond_reglement_id = $obj->fk_cond_reglement; |
591
|
|
|
$this->cond_reglement_code = $obj->cond_reglement_code; |
592
|
|
|
$this->cond_reglement = $obj->cond_reglement; |
593
|
|
|
$this->cond_reglement_doc = $obj->cond_reglement_libelle_doc; |
594
|
|
|
$this->deposit_percent = $obj->deposit_percent; |
595
|
|
|
|
596
|
|
|
$this->extraparams = !empty($obj->extraparams) ? (array)json_decode($obj->extraparams, true) : array(); |
597
|
|
|
|
598
|
|
|
$this->user_author_id = $obj->fk_user_author; |
599
|
|
|
$this->user_validation_id = $obj->fk_user_valid; |
600
|
|
|
$this->user_closing_id = $obj->fk_user_cloture; |
601
|
|
|
|
602
|
|
|
//Incoterms |
603
|
|
|
$this->fk_incoterms = $obj->fk_incoterms; |
604
|
|
|
$this->location_incoterms = $obj->location_incoterms; |
605
|
|
|
$this->label_incoterms = $obj->label_incoterms; |
606
|
|
|
|
607
|
|
|
// Multicurrency |
608
|
|
|
$this->fk_multicurrency = $obj->fk_multicurrency; |
609
|
|
|
$this->multicurrency_code = $obj->multicurrency_code; |
610
|
|
|
$this->multicurrency_tx = $obj->multicurrency_tx; |
611
|
|
|
$this->multicurrency_total_ht = $obj->multicurrency_total_ht; |
612
|
|
|
$this->multicurrency_total_tva = $obj->multicurrency_total_tva; |
613
|
|
|
$this->multicurrency_total_ttc = $obj->multicurrency_total_ttc; |
614
|
|
|
|
615
|
|
|
// Retrieve all extrafield |
616
|
|
|
// fetch optionals attributes and labels |
617
|
|
|
$this->fetch_optionals(); |
618
|
|
|
|
619
|
|
|
$this->db->free($resql); |
620
|
|
|
|
621
|
|
|
$this->lines = array(); |
622
|
|
|
|
623
|
|
|
// Lines |
624
|
|
|
$result = $this->fetch_lines(); |
625
|
|
|
if ($result < 0) { |
626
|
|
|
return -3; |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
return 1; |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
$this->error = "Record Not Found"; |
633
|
|
|
return 0; |
634
|
|
|
} else { |
635
|
|
|
$this->error = $this->db->lasterror(); |
636
|
|
|
return -1; |
637
|
|
|
} |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Load array lines |
642
|
|
|
* |
643
|
|
|
* @param int $only_product Return only physical products |
644
|
|
|
* @param int $loadalsotranslation Return translation for products |
645
|
|
|
* @param string $sqlforgedfilters Filter on other fields |
646
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
647
|
|
|
*/ |
648
|
|
|
public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '') |
649
|
|
|
{ |
650
|
|
|
// phpcs:enable |
651
|
|
|
$this->lines = array(); |
652
|
|
|
|
653
|
|
|
$sql = 'SELECT d.rowid, d.fk_propal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,'; |
654
|
|
|
$sql .= ' d.info_bits, d.total_ht, d.total_tva, d.total_localtax1, d.total_localtax2, d.total_ttc, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht, d.special_code, d.rang, d.product_type,'; |
655
|
|
|
$sql .= ' d.fk_unit,'; |
656
|
|
|
$sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch, p.barcode as product_barcode,'; |
657
|
|
|
$sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,'; |
658
|
|
|
$sql .= ' d.date_start, d.date_end,'; |
659
|
|
|
$sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc'; |
660
|
|
|
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'propaldet as d'; |
661
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON (d.fk_product = p.rowid)'; |
662
|
|
|
$sql .= ' WHERE d.fk_propal = ' . ((int)$this->id); |
663
|
|
|
if ($only_product) { |
664
|
|
|
$sql .= ' AND p.fk_product_type = 0'; |
665
|
|
|
} |
666
|
|
|
if ($sqlforgedfilters) { |
667
|
|
|
$sql .= $sqlforgedfilters; |
668
|
|
|
} |
669
|
|
|
$sql .= ' ORDER by d.rang'; |
670
|
|
|
|
671
|
|
|
dol_syslog(get_only_class($this) . "::fetch_lines", LOG_DEBUG); |
672
|
|
|
$result = $this->db->query($sql); |
673
|
|
|
if ($result) { |
674
|
|
|
$num = $this->db->num_rows($result); |
675
|
|
|
|
676
|
|
|
$i = 0; |
677
|
|
|
while ($i < $num) { |
678
|
|
|
$objp = $this->db->fetch_object($result); |
679
|
|
|
|
680
|
|
|
$line = new PropaleLigne($this->db); |
681
|
|
|
|
682
|
|
|
$line->rowid = $objp->rowid; //Deprecated |
|
|
|
|
683
|
|
|
$line->id = $objp->rowid; |
684
|
|
|
$line->fk_propal = $objp->fk_propal; |
685
|
|
|
$line->fk_parent_line = $objp->fk_parent_line; |
686
|
|
|
$line->product_type = $objp->product_type; |
687
|
|
|
$line->label = $objp->custom_label; |
688
|
|
|
$line->desc = $objp->description; // Description ligne |
689
|
|
|
$line->description = $objp->description; // Description ligne |
690
|
|
|
$line->qty = $objp->qty; |
691
|
|
|
$line->vat_src_code = $objp->vat_src_code; |
692
|
|
|
$line->tva_tx = $objp->tva_tx; |
693
|
|
|
$line->localtax1_tx = $objp->localtax1_tx; |
694
|
|
|
$line->localtax2_tx = $objp->localtax2_tx; |
695
|
|
|
$line->localtax1_type = $objp->localtax1_type; |
696
|
|
|
$line->localtax2_type = $objp->localtax2_type; |
697
|
|
|
$line->subprice = $objp->subprice; |
698
|
|
|
$line->fk_remise_except = $objp->fk_remise_except; |
699
|
|
|
$line->remise_percent = $objp->remise_percent; |
700
|
|
|
$line->price = $objp->price; // TODO deprecated |
701
|
|
|
|
702
|
|
|
$line->info_bits = $objp->info_bits; |
703
|
|
|
$line->total_ht = $objp->total_ht; |
704
|
|
|
$line->total_tva = $objp->total_tva; |
705
|
|
|
$line->total_localtax1 = $objp->total_localtax1; |
706
|
|
|
$line->total_localtax2 = $objp->total_localtax2; |
707
|
|
|
$line->total_ttc = $objp->total_ttc; |
708
|
|
|
$line->fk_fournprice = $objp->fk_fournprice; |
709
|
|
|
$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht); |
710
|
|
|
$line->pa_ht = $marginInfos[0]; |
711
|
|
|
$line->marge_tx = $marginInfos[1]; |
712
|
|
|
$line->marque_tx = $marginInfos[2]; |
713
|
|
|
$line->special_code = $objp->special_code; |
714
|
|
|
$line->rang = $objp->rang; |
715
|
|
|
|
716
|
|
|
$line->fk_product = $objp->fk_product; |
717
|
|
|
|
718
|
|
|
$line->ref = $objp->product_ref; // deprecated |
719
|
|
|
$line->libelle = $objp->product_label; // deprecated |
720
|
|
|
|
721
|
|
|
$line->product_ref = $objp->product_ref; |
722
|
|
|
$line->product_label = $objp->product_label; |
723
|
|
|
$line->product_desc = $objp->product_desc; // Description produit |
724
|
|
|
$line->product_tobatch = $objp->product_tobatch; |
725
|
|
|
$line->product_barcode = $objp->product_barcode; |
726
|
|
|
|
727
|
|
|
$line->fk_product_type = $objp->fk_product_type; // deprecated |
728
|
|
|
$line->fk_unit = $objp->fk_unit; |
729
|
|
|
$line->weight = $objp->weight; |
730
|
|
|
$line->weight_units = $objp->weight_units; |
731
|
|
|
$line->volume = $objp->volume; |
732
|
|
|
$line->volume_units = $objp->volume_units; |
733
|
|
|
|
734
|
|
|
$line->date_start = $this->db->jdate($objp->date_start); |
735
|
|
|
$line->date_end = $this->db->jdate($objp->date_end); |
736
|
|
|
|
737
|
|
|
// Multicurrency |
738
|
|
|
$line->fk_multicurrency = $objp->fk_multicurrency; |
739
|
|
|
$line->multicurrency_code = $objp->multicurrency_code; |
740
|
|
|
$line->multicurrency_subprice = $objp->multicurrency_subprice; |
741
|
|
|
$line->multicurrency_total_ht = $objp->multicurrency_total_ht; |
742
|
|
|
$line->multicurrency_total_tva = $objp->multicurrency_total_tva; |
743
|
|
|
$line->multicurrency_total_ttc = $objp->multicurrency_total_ttc; |
744
|
|
|
|
745
|
|
|
$line->fetch_optionals(); |
746
|
|
|
|
747
|
|
|
// multilangs |
748
|
|
|
if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) { |
749
|
|
|
$tmpproduct = new Product($this->db); |
750
|
|
|
$tmpproduct->fetch($objp->fk_product); |
751
|
|
|
$tmpproduct->getMultiLangs(); |
752
|
|
|
|
753
|
|
|
$line->multilangs = $tmpproduct->multilangs; |
754
|
|
|
} |
755
|
|
|
|
756
|
|
|
$this->lines[$i] = $line; |
757
|
|
|
|
758
|
|
|
$i++; |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
$this->db->free($result); |
762
|
|
|
|
763
|
|
|
return $num; |
764
|
|
|
} else { |
765
|
|
|
$this->error = $this->db->lasterror(); |
766
|
|
|
return -3; |
767
|
|
|
} |
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
/** |
771
|
|
|
* Adding line of fixed discount in the proposal in DB |
772
|
|
|
* |
773
|
|
|
* @param int $idremise Id of fixed discount |
774
|
|
|
* @return int >0 if OK, <0 if KO |
775
|
|
|
*/ |
776
|
|
|
public function insert_discount($idremise) |
777
|
|
|
{ |
778
|
|
|
// phpcs:enable |
779
|
|
|
global $langs; |
780
|
|
|
|
781
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
782
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php'; |
783
|
|
|
|
784
|
|
|
$this->db->begin(); |
785
|
|
|
|
786
|
|
|
$remise = new DiscountAbsolute($this->db); |
787
|
|
|
$result = $remise->fetch($idremise); |
788
|
|
|
|
789
|
|
|
if ($result > 0) { |
790
|
|
|
if ($remise->fk_facture) { // Protection against multiple submission |
791
|
|
|
$this->error = $langs->trans("ErrorDiscountAlreadyUsed"); |
792
|
|
|
$this->db->rollback(); |
793
|
|
|
return -5; |
794
|
|
|
} |
795
|
|
|
|
796
|
|
|
$line = new PropaleLigne($this->db); |
797
|
|
|
|
798
|
|
|
$line->context = $this->context; |
799
|
|
|
|
800
|
|
|
$line->fk_propal = $this->id; |
801
|
|
|
$line->fk_remise_except = $remise->id; |
802
|
|
|
$line->desc = $remise->description; // Description ligne |
803
|
|
|
$line->vat_src_code = $remise->vat_src_code; |
804
|
|
|
$line->tva_tx = $remise->tva_tx; |
805
|
|
|
$line->subprice = -$remise->amount_ht; |
806
|
|
|
$line->fk_product = 0; // Id produit predefined |
807
|
|
|
$line->qty = 1; |
808
|
|
|
$line->remise_percent = 0; |
809
|
|
|
$line->rang = -1; |
810
|
|
|
$line->info_bits = 2; |
811
|
|
|
|
812
|
|
|
// TODO deprecated |
813
|
|
|
$line->price = -$remise->amount_ht; |
814
|
|
|
|
815
|
|
|
$line->total_ht = -$remise->amount_ht; |
816
|
|
|
$line->total_tva = -$remise->amount_tva; |
817
|
|
|
$line->total_ttc = -$remise->amount_ttc; |
818
|
|
|
|
819
|
|
|
$result = $line->insert(); |
820
|
|
|
if ($result > 0) { |
821
|
|
|
$result = $this->update_price(1); |
822
|
|
|
if ($result > 0) { |
823
|
|
|
$this->db->commit(); |
824
|
|
|
return 1; |
825
|
|
|
} else { |
826
|
|
|
$this->db->rollback(); |
827
|
|
|
return -1; |
828
|
|
|
} |
829
|
|
|
} else { |
830
|
|
|
$this->error = $line->error; |
831
|
|
|
$this->errors = $line->errors; |
832
|
|
|
$this->db->rollback(); |
833
|
|
|
return -2; |
834
|
|
|
} |
835
|
|
|
} else { |
836
|
|
|
$this->db->rollback(); |
837
|
|
|
return -2; |
838
|
|
|
} |
839
|
|
|
} |
840
|
|
|
|
841
|
|
|
/** |
842
|
|
|
* Update a proposal line |
843
|
|
|
* |
844
|
|
|
* @param int $rowid Id of line |
845
|
|
|
* @param float $pu Unit price (HT or TTC depending on price_base_type) |
846
|
|
|
* @param float $qty Quantity |
847
|
|
|
* @param float $remise_percent Discount on line |
848
|
|
|
* @param float|string $txtva VAT Rate (Can be '1.23' or '1.23 (ABC)') |
849
|
|
|
* @param float $txlocaltax1 Local tax 1 rate |
850
|
|
|
* @param float $txlocaltax2 Local tax 2 rate |
851
|
|
|
* @param string $desc Description |
852
|
|
|
* @param string $price_base_type HT or TTC |
853
|
|
|
* @param int $info_bits Miscellaneous information |
854
|
|
|
* @param int $special_code Special code (also used by externals modules!) |
855
|
|
|
* @param int $fk_parent_line Id of parent line (0 in most cases, used by modules adding sublevels into lines). |
856
|
|
|
* @param int $skip_update_total Keep fields total_xxx to 0 (used for special lines by some modules) |
857
|
|
|
* @param int $fk_fournprice Id of origin supplier price |
858
|
|
|
* @param int $pa_ht Price (without tax) of product when it was bought |
859
|
|
|
* @param string $label ??? |
860
|
|
|
* @param int $type 0/1=Product/service |
861
|
|
|
* @param int|string $date_start Start date of the line |
862
|
|
|
* @param int|string $date_end End date of the line |
863
|
|
|
* @param array $array_options extrafields array |
864
|
|
|
* @param int|null $fk_unit Code of the unit to use. Null to use the default one |
865
|
|
|
* @param double $pu_ht_devise Unit price in currency |
866
|
|
|
* @param int $notrigger disable line update trigger |
867
|
|
|
* @param integer $rang line rank |
868
|
|
|
* @return int 0 if OK, <0 if KO |
869
|
|
|
*/ |
870
|
|
|
public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $desc = '', $price_base_type = 'HT', $info_bits = 0, $special_code = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $type = 0, $date_start = '', $date_end = '', $array_options = array(), $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $rang = 0) |
871
|
|
|
{ |
872
|
|
|
global $mysoc, $langs; |
873
|
|
|
|
874
|
|
|
dol_syslog(get_only_class($this) . "::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent, |
875
|
|
|
txtva=$txtva, desc=$desc, price_base_type=$price_base_type, info_bits=$info_bits, special_code=$special_code, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, type=$type, date_start=$date_start, date_end=$date_end"); |
876
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
877
|
|
|
|
878
|
|
|
// Clean parameters |
879
|
|
|
$remise_percent = price2num($remise_percent); |
880
|
|
|
$qty = (float)price2num($qty); |
881
|
|
|
$pu = price2num($pu); |
882
|
|
|
$pu_ht_devise = price2num($pu_ht_devise); |
883
|
|
|
if (!preg_match('/\((.*)\)/', (string)$txtva)) { |
884
|
|
|
$txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5' |
885
|
|
|
} |
886
|
|
|
$txlocaltax1 = price2num($txlocaltax1); |
887
|
|
|
$txlocaltax2 = price2num($txlocaltax2); |
888
|
|
|
$pa_ht = price2num($pa_ht); |
889
|
|
|
if (empty($qty) && empty($special_code)) { |
890
|
|
|
$special_code = 3; // Set option tag |
891
|
|
|
} |
892
|
|
|
if (!empty($qty) && $special_code == 3) { |
893
|
|
|
$special_code = 0; // Remove option tag |
894
|
|
|
} |
895
|
|
|
if (empty($type)) { |
896
|
|
|
$type = 0; |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
if ($date_start && $date_end && $date_start > $date_end) { |
900
|
|
|
$langs->load("errors"); |
901
|
|
|
$this->error = $langs->trans('ErrorStartDateGreaterEnd'); |
902
|
|
|
return -1; |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
if ($this->status == self::STATUS_DRAFT) { |
906
|
|
|
$this->db->begin(); |
907
|
|
|
|
908
|
|
|
// Calcul du total TTC et de la TVA pour la ligne a partir de |
909
|
|
|
// qty, pu, remise_percent et txtva |
910
|
|
|
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker |
911
|
|
|
// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. |
912
|
|
|
|
913
|
|
|
$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc); |
914
|
|
|
|
915
|
|
|
// Clean vat code |
916
|
|
|
$reg = array(); |
917
|
|
|
$vat_src_code = ''; |
918
|
|
|
if (preg_match('/\((.*)\)/', $txtva, $reg)) { |
919
|
|
|
$vat_src_code = $reg[1]; |
920
|
|
|
$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate. |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
// TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ? |
924
|
|
|
|
925
|
|
|
$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise); |
926
|
|
|
$total_ht = $tabprice[0]; |
927
|
|
|
$total_tva = $tabprice[1]; |
928
|
|
|
$total_ttc = $tabprice[2]; |
929
|
|
|
$total_localtax1 = $tabprice[9]; |
930
|
|
|
$total_localtax2 = $tabprice[10]; |
931
|
|
|
$pu_ht = $tabprice[3]; |
932
|
|
|
$pu_tva = $tabprice[4]; |
933
|
|
|
$pu_ttc = $tabprice[5]; |
934
|
|
|
|
935
|
|
|
// MultiCurrency |
936
|
|
|
$multicurrency_total_ht = $tabprice[16]; |
937
|
|
|
$multicurrency_total_tva = $tabprice[17]; |
938
|
|
|
$multicurrency_total_ttc = $tabprice[18]; |
939
|
|
|
$pu_ht_devise = $tabprice[19]; |
940
|
|
|
|
941
|
|
|
// Anciens indicateurs: $price, $remise (a ne plus utiliser) |
942
|
|
|
$price = $pu; |
943
|
|
|
$remise = 0; |
944
|
|
|
if ((float)$remise_percent > 0) { |
945
|
|
|
$remise = round(((float)$pu * (float)$remise_percent / 100), 2); |
946
|
|
|
$price = (float)$pu - $remise; |
947
|
|
|
} |
948
|
|
|
|
949
|
|
|
//Fetch current line from the database and then clone the object and set it in $oldline property |
950
|
|
|
$line = new PropaleLigne($this->db); |
951
|
|
|
$line->fetch($rowid); |
952
|
|
|
|
953
|
|
|
$staticline = clone $line; |
954
|
|
|
|
955
|
|
|
$line->oldline = $staticline; |
956
|
|
|
$this->line = $line; |
957
|
|
|
$this->line->context = $this->context; |
958
|
|
|
$this->line->rang = $rang; |
959
|
|
|
|
960
|
|
|
// Reorder if fk_parent_line change |
961
|
|
|
if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) { |
962
|
|
|
$rangmax = $this->line_max($fk_parent_line); |
963
|
|
|
$this->line->rang = $rangmax + 1; |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
$this->line->id = $rowid; |
967
|
|
|
$this->line->label = $label; |
968
|
|
|
$this->line->desc = $desc; |
969
|
|
|
$this->line->qty = $qty; |
970
|
|
|
$this->line->product_type = $type; |
971
|
|
|
$this->line->vat_src_code = $vat_src_code; |
972
|
|
|
$this->line->tva_tx = $txtva; |
973
|
|
|
$this->line->localtax1_tx = $txlocaltax1; |
974
|
|
|
$this->line->localtax2_tx = $txlocaltax2; |
975
|
|
|
$this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0]; |
976
|
|
|
$this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2]; |
977
|
|
|
$this->line->remise_percent = $remise_percent; |
978
|
|
|
$this->line->subprice = $pu_ht; |
979
|
|
|
$this->line->info_bits = $info_bits; |
980
|
|
|
|
981
|
|
|
$this->line->total_ht = $total_ht; |
982
|
|
|
$this->line->total_tva = $total_tva; |
983
|
|
|
$this->line->total_localtax1 = $total_localtax1; |
984
|
|
|
$this->line->total_localtax2 = $total_localtax2; |
985
|
|
|
$this->line->total_ttc = $total_ttc; |
986
|
|
|
$this->line->special_code = $special_code; |
987
|
|
|
$this->line->fk_parent_line = $fk_parent_line; |
988
|
|
|
$this->line->skip_update_total = $skip_update_total; |
989
|
|
|
$this->line->fk_unit = $fk_unit; |
990
|
|
|
|
991
|
|
|
$this->line->fk_fournprice = $fk_fournprice; |
992
|
|
|
$this->line->pa_ht = $pa_ht; |
993
|
|
|
|
994
|
|
|
$this->line->date_start = $date_start; |
995
|
|
|
$this->line->date_end = $date_end; |
996
|
|
|
|
997
|
|
|
if (is_array($array_options) && count($array_options) > 0) { |
998
|
|
|
// We replace values in this->line->array_options only for entries defined into $array_options |
999
|
|
|
foreach ($array_options as $key => $value) { |
1000
|
|
|
$this->line->array_options[$key] = $array_options[$key]; |
1001
|
|
|
} |
1002
|
|
|
} |
1003
|
|
|
|
1004
|
|
|
// Multicurrency |
1005
|
|
|
$this->line->multicurrency_subprice = $pu_ht_devise; |
1006
|
|
|
$this->line->multicurrency_total_ht = $multicurrency_total_ht; |
1007
|
|
|
$this->line->multicurrency_total_tva = $multicurrency_total_tva; |
1008
|
|
|
$this->line->multicurrency_total_ttc = $multicurrency_total_ttc; |
1009
|
|
|
|
1010
|
|
|
$result = $this->line->update($notrigger); |
1011
|
|
|
if ($result > 0) { |
1012
|
|
|
// Reorder if child line |
1013
|
|
|
if (!empty($fk_parent_line)) { |
1014
|
|
|
$this->line_order(true, 'DESC'); |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
$this->update_price(1, 'auto'); |
1018
|
|
|
|
1019
|
|
|
// $this is Propal |
1020
|
|
|
// $this->fk_propal = $this->id; |
1021
|
|
|
// $this->rowid = $rowid; |
1022
|
|
|
|
1023
|
|
|
$this->db->commit(); |
1024
|
|
|
return $result; |
1025
|
|
|
} else { |
1026
|
|
|
$this->error = $this->line->error; |
1027
|
|
|
$this->errors = $this->line->errors; |
1028
|
|
|
$this->db->rollback(); |
1029
|
|
|
return -1; |
1030
|
|
|
} |
1031
|
|
|
} else { |
1032
|
|
|
dol_syslog(get_only_class($this) . "::updateline Erreur -2 Propal en mode incompatible pour cette action"); |
1033
|
|
|
return -2; |
1034
|
|
|
} |
1035
|
|
|
} |
1036
|
|
|
|
1037
|
|
|
/** |
1038
|
|
|
* Update database |
1039
|
|
|
* |
1040
|
|
|
* @param User $user User that modify |
1041
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
1042
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1043
|
|
|
*/ |
1044
|
|
|
public function update(User $user, $notrigger = 0) |
1045
|
|
|
{ |
1046
|
|
|
global $conf; |
1047
|
|
|
|
1048
|
|
|
$error = 0; |
1049
|
|
|
|
1050
|
|
|
// Clean parameters |
1051
|
|
|
if (isset($this->ref)) { |
1052
|
|
|
$this->ref = trim($this->ref); |
1053
|
|
|
} |
1054
|
|
|
if (isset($this->ref_client)) { |
1055
|
|
|
$this->ref_client = trim($this->ref_client); |
1056
|
|
|
} |
1057
|
|
|
if (isset($this->note) || isset($this->note_private)) { |
1058
|
|
|
$this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note)); |
1059
|
|
|
} |
1060
|
|
|
if (isset($this->note_public)) { |
1061
|
|
|
$this->note_public = trim($this->note_public); |
1062
|
|
|
} |
1063
|
|
|
if (isset($this->model_pdf)) { |
1064
|
|
|
$this->model_pdf = trim($this->model_pdf); |
1065
|
|
|
} |
1066
|
|
|
if (isset($this->import_key)) { |
1067
|
|
|
$this->import_key = trim($this->import_key); |
1068
|
|
|
} |
1069
|
|
|
if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) { |
1070
|
|
|
$this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600); |
1071
|
|
|
} |
1072
|
|
|
|
1073
|
|
|
// Check parameters |
1074
|
|
|
// Put here code to add control on parameters values |
1075
|
|
|
|
1076
|
|
|
// Update request |
1077
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET"; |
1078
|
|
|
$sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ","; |
1079
|
|
|
$sql .= " ref_client=" . (isset($this->ref_client) ? "'" . $this->db->escape($this->ref_client) . "'" : "null") . ","; |
1080
|
|
|
$sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ","; |
1081
|
|
|
$sql .= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ","; |
1082
|
|
|
$sql .= " datep=" . (strval($this->date) != '' ? "'" . $this->db->idate($this->date) . "'" : 'null') . ","; |
1083
|
|
|
if (!empty($this->fin_validite)) { |
1084
|
|
|
$sql .= " fin_validite=" . (strval($this->fin_validite) != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : 'null') . ","; |
1085
|
|
|
} |
1086
|
|
|
$sql .= " date_valid=" . (strval($this->date_validation) != '' ? "'" . $this->db->idate($this->date_validation) . "'" : 'null') . ","; |
1087
|
|
|
$sql .= " total_tva=" . (isset($this->total_tva) ? $this->total_tva : "null") . ","; |
1088
|
|
|
$sql .= " localtax1=" . (isset($this->total_localtax1) ? $this->total_localtax1 : "null") . ","; |
1089
|
|
|
$sql .= " localtax2=" . (isset($this->total_localtax2) ? $this->total_localtax2 : "null") . ","; |
1090
|
|
|
$sql .= " total_ht=" . (isset($this->total_ht) ? $this->total_ht : "null") . ","; |
1091
|
|
|
$sql .= " total_ttc=" . (isset($this->total_ttc) ? $this->total_ttc : "null") . ","; |
1092
|
|
|
$sql .= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ","; |
1093
|
|
|
$sql .= " fk_user_author=" . (isset($this->user_author_id) ? $this->user_author_id : "null") . ","; |
1094
|
|
|
$sql .= " fk_user_valid=" . (isset($this->user_validation_id) ? $this->user_validation_id : "null") . ","; |
1095
|
|
|
$sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ","; |
1096
|
|
|
$sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null") . ","; |
1097
|
|
|
$sql .= " deposit_percent=" . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : "null") . ","; |
1098
|
|
|
$sql .= " fk_mode_reglement=" . (isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null") . ","; |
1099
|
|
|
$sql .= " fk_input_reason=" . (isset($this->demand_reason_id) ? $this->demand_reason_id : "null") . ","; |
1100
|
|
|
$sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ","; |
1101
|
|
|
$sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ","; |
1102
|
|
|
$sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ","; |
1103
|
|
|
$sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null"); |
1104
|
|
|
$sql .= " WHERE rowid=" . ((int)$this->id); |
1105
|
|
|
|
1106
|
|
|
$this->db->begin(); |
1107
|
|
|
|
1108
|
|
|
dol_syslog(get_only_class($this) . "::update", LOG_DEBUG); |
1109
|
|
|
$resql = $this->db->query($sql); |
1110
|
|
|
if (!$resql) { |
1111
|
|
|
$error++; |
1112
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
|
|
if (!$error) { |
1116
|
|
|
$result = $this->insertExtraFields(); |
1117
|
|
|
if ($result < 0) { |
1118
|
|
|
$error++; |
1119
|
|
|
} |
1120
|
|
|
} |
1121
|
|
|
|
1122
|
|
|
if (!$error && !$notrigger) { |
1123
|
|
|
// Call trigger |
1124
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
1125
|
|
|
if ($result < 0) { |
1126
|
|
|
$error++; |
1127
|
|
|
} |
1128
|
|
|
// End call triggers |
1129
|
|
|
} |
1130
|
|
|
|
1131
|
|
|
// Commit or rollback |
1132
|
|
|
if ($error) { |
1133
|
|
|
foreach ($this->errors as $errmsg) { |
1134
|
|
|
dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR); |
1135
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
1136
|
|
|
} |
1137
|
|
|
$this->db->rollback(); |
1138
|
|
|
return -1 * $error; |
1139
|
|
|
} else { |
1140
|
|
|
$this->db->commit(); |
1141
|
|
|
return 1; |
1142
|
|
|
} |
1143
|
|
|
} |
1144
|
|
|
|
1145
|
|
|
/** |
1146
|
|
|
* Delete detail line |
1147
|
|
|
* |
1148
|
|
|
* @param int $lineid Id of line to delete |
1149
|
|
|
* @param int $id Id of object (for a check) |
1150
|
|
|
* @return int >0 if OK, <0 if KO |
1151
|
|
|
*/ |
1152
|
|
|
public function deleteLine($lineid, $id = 0) |
1153
|
|
|
{ |
1154
|
|
|
global $user; |
1155
|
|
|
|
1156
|
|
|
if ($this->statut == self::STATUS_DRAFT) { |
1157
|
|
|
$this->db->begin(); |
1158
|
|
|
|
1159
|
|
|
$line = new PropaleLigne($this->db); |
1160
|
|
|
|
1161
|
|
|
$line->context = $this->context; |
1162
|
|
|
|
1163
|
|
|
// Load data |
1164
|
|
|
$line->fetch($lineid); |
1165
|
|
|
|
1166
|
|
|
if ($id > 0 && $line->fk_propal != $id) { |
1167
|
|
|
$this->error = 'ErrorLineIDDoesNotMatchWithObjectID'; |
1168
|
|
|
return -1; |
1169
|
|
|
} |
1170
|
|
|
|
1171
|
|
|
// Memorize previous line for triggers |
1172
|
|
|
$staticline = clone $line; |
1173
|
|
|
$line->oldline = $staticline; |
1174
|
|
|
|
1175
|
|
|
if ($line->delete($user) > 0) { |
1176
|
|
|
$this->update_price(1); |
1177
|
|
|
|
1178
|
|
|
$this->db->commit(); |
1179
|
|
|
return 1; |
1180
|
|
|
} else { |
1181
|
|
|
$this->error = $line->error; |
1182
|
|
|
$this->errors = $line->errors; |
1183
|
|
|
$this->db->rollback(); |
1184
|
|
|
return -1; |
1185
|
|
|
} |
1186
|
|
|
} else { |
1187
|
|
|
$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus'; |
1188
|
|
|
return -2; |
1189
|
|
|
} |
1190
|
|
|
} |
1191
|
|
|
|
1192
|
|
|
|
1193
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
1194
|
|
|
|
1195
|
|
|
/** |
1196
|
|
|
* Delete proposal |
1197
|
|
|
* |
1198
|
|
|
* @param User $user Object user that delete |
1199
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
1200
|
|
|
* @return int >0 if OK, <=0 if KO |
1201
|
|
|
*/ |
1202
|
|
|
public function delete($user, $notrigger = 0) |
1203
|
|
|
{ |
1204
|
|
|
global $conf; |
1205
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
1206
|
|
|
|
1207
|
|
|
$error = 0; |
1208
|
|
|
|
1209
|
|
|
$this->db->begin(); |
1210
|
|
|
|
1211
|
|
|
if (!$notrigger) { |
1212
|
|
|
// Call trigger |
1213
|
|
|
$result = $this->call_trigger('PROPAL_DELETE', $user); |
1214
|
|
|
if ($result < 0) { |
1215
|
|
|
$error++; |
1216
|
|
|
} |
1217
|
|
|
// End call triggers |
1218
|
|
|
} |
1219
|
|
|
|
1220
|
|
|
// Delete extrafields of lines and lines |
1221
|
|
|
if (!$error && !empty($this->table_element_line)) { |
1222
|
|
|
$tabletodelete = $this->table_element_line; |
1223
|
|
|
$sqlef = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . "_extrafields WHERE fk_object IN (SELECT rowid FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int)$this->id) . ")"; |
1224
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int)$this->id); |
1225
|
|
|
if (!$this->db->query($sqlef) || !$this->db->query($sql)) { |
1226
|
|
|
$error++; |
1227
|
|
|
$this->error = $this->db->lasterror(); |
1228
|
|
|
$this->errors[] = $this->error; |
1229
|
|
|
dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR); |
1230
|
|
|
} |
1231
|
|
|
} |
1232
|
|
|
|
1233
|
|
|
if (!$error) { |
1234
|
|
|
// Delete linked object |
1235
|
|
|
$res = $this->deleteObjectLinked(); |
1236
|
|
|
if ($res < 0) { |
1237
|
|
|
$error++; |
1238
|
|
|
} |
1239
|
|
|
} |
1240
|
|
|
|
1241
|
|
|
if (!$error) { |
1242
|
|
|
// Delete linked contacts |
1243
|
|
|
$res = $this->delete_linked_contact(); |
1244
|
|
|
if ($res < 0) { |
1245
|
|
|
$error++; |
1246
|
|
|
} |
1247
|
|
|
} |
1248
|
|
|
|
1249
|
|
|
// Removed extrafields of object |
1250
|
|
|
if (!$error) { |
1251
|
|
|
$result = $this->deleteExtraFields(); |
1252
|
|
|
if ($result < 0) { |
1253
|
|
|
$error++; |
1254
|
|
|
dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR); |
1255
|
|
|
} |
1256
|
|
|
} |
1257
|
|
|
|
1258
|
|
|
// Delete main record |
1259
|
|
|
if (!$error) { |
1260
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int)$this->id); |
1261
|
|
|
$res = $this->db->query($sql); |
1262
|
|
|
if (!$res) { |
1263
|
|
|
$error++; |
1264
|
|
|
$this->error = $this->db->lasterror(); |
1265
|
|
|
$this->errors[] = $this->error; |
1266
|
|
|
dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR); |
1267
|
|
|
} |
1268
|
|
|
} |
1269
|
|
|
|
1270
|
|
|
// Delete record into ECM index and physically |
1271
|
|
|
if (!$error) { |
1272
|
|
|
$res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive |
1273
|
|
|
$res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive |
1274
|
|
|
if (!$res) { |
1275
|
|
|
$error++; |
1276
|
|
|
} |
1277
|
|
|
} |
1278
|
|
|
|
1279
|
|
|
if (!$error) { |
1280
|
|
|
// We remove directory |
1281
|
|
|
$ref = dol_sanitizeFileName($this->ref); |
1282
|
|
|
if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) { |
1283
|
|
|
$dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref; |
1284
|
|
|
$file = $dir . "/" . $ref . ".pdf"; |
1285
|
|
|
if (file_exists($file)) { |
1286
|
|
|
dol_delete_preview($this); |
1287
|
|
|
|
1288
|
|
|
if (!dol_delete_file($file, 0, 0, 0, $this)) { |
1289
|
|
|
$this->error = 'ErrorFailToDeleteFile'; |
1290
|
|
|
$this->errors[] = $this->error; |
1291
|
|
|
$this->db->rollback(); |
1292
|
|
|
return 0; |
1293
|
|
|
} |
1294
|
|
|
} |
1295
|
|
|
if (file_exists($dir)) { |
1296
|
|
|
$res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables |
1297
|
|
|
if (!$res) { |
1298
|
|
|
$this->error = 'ErrorFailToDeleteDir'; |
1299
|
|
|
$this->errors[] = $this->error; |
1300
|
|
|
$this->db->rollback(); |
1301
|
|
|
return 0; |
1302
|
|
|
} |
1303
|
|
|
} |
1304
|
|
|
} |
1305
|
|
|
} |
1306
|
|
|
|
1307
|
|
|
if (!$error) { |
1308
|
|
|
dol_syslog(get_only_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG); |
1309
|
|
|
$this->db->commit(); |
1310
|
|
|
return 1; |
1311
|
|
|
} else { |
1312
|
|
|
$this->db->rollback(); |
1313
|
|
|
return -1; |
1314
|
|
|
} |
1315
|
|
|
} |
1316
|
|
|
|
1317
|
|
|
/** |
1318
|
|
|
* Load an object from its id and create a new one in database |
1319
|
|
|
* |
1320
|
|
|
* @param User $user User making the clone |
1321
|
|
|
* @param int $socid Id of thirdparty |
1322
|
|
|
* @param int $forceentity Entity id to force |
1323
|
|
|
* @param bool $update_prices [=false] Update prices if true |
1324
|
|
|
* @param bool $update_desc [=false] Update description if true |
1325
|
|
|
* @return int New id of clone |
1326
|
|
|
*/ |
1327
|
|
|
public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false) |
1328
|
|
|
{ |
1329
|
|
|
global $conf, $hookmanager, $mysoc; |
1330
|
|
|
|
1331
|
|
|
dol_include_once('/projet/class/project.class.php'); |
1332
|
|
|
|
1333
|
|
|
$error = 0; |
1334
|
|
|
$now = dol_now(); |
1335
|
|
|
|
1336
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
1337
|
|
|
|
1338
|
|
|
$object = new self($this->db); |
1339
|
|
|
|
1340
|
|
|
$this->db->begin(); |
1341
|
|
|
|
1342
|
|
|
// Load source object |
1343
|
|
|
$object->fetch($this->id); |
1344
|
|
|
|
1345
|
|
|
$objsoc = new Societe($this->db); |
1346
|
|
|
|
1347
|
|
|
// Change socid if needed |
1348
|
|
|
if (!empty($socid) && $socid != $object->socid) { |
1349
|
|
|
if ($objsoc->fetch($socid) > 0) { |
1350
|
|
|
$object->socid = $objsoc->id; |
1351
|
|
|
$object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0); |
1352
|
|
|
$object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null); |
1353
|
|
|
$object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0); |
1354
|
|
|
$object->fk_delivery_address = 0; |
1355
|
|
|
|
1356
|
|
|
/*if (isModEnabled('project')) |
1357
|
|
|
{ |
1358
|
|
|
$project = new Project($db); |
1359
|
|
|
if ($this->fk_project > 0 && $project->fetch($this->fk_project)) { |
1360
|
|
|
if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project; |
1361
|
|
|
else $clonedObj->fk_project = ''; |
1362
|
|
|
} else { |
1363
|
|
|
$clonedObj->fk_project = ''; |
1364
|
|
|
} |
1365
|
|
|
}*/ |
1366
|
|
|
$object->fk_project = 0; // A cloned proposal is set by default to no project. |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
|
|
// reset ref_client |
1370
|
|
|
$object->ref_client = ''; |
1371
|
|
|
|
1372
|
|
|
// TODO Change product price if multi-prices |
1373
|
|
|
} else { |
1374
|
|
|
$objsoc->fetch($object->socid); |
1375
|
|
|
} |
1376
|
|
|
|
1377
|
|
|
// update prices |
1378
|
|
|
if ($update_prices === true || $update_desc === true) { |
1379
|
|
|
if ($objsoc->id > 0 && !empty($object->lines)) { |
1380
|
|
|
if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) { |
1381
|
|
|
// If price per customer |
1382
|
|
|
} |
1383
|
|
|
|
1384
|
|
|
foreach ($object->lines as $line) { |
1385
|
|
|
$line->id = 0; |
1386
|
|
|
|
1387
|
|
|
if ($line->fk_product > 0) { |
1388
|
|
|
$prod = new Product($this->db); |
1389
|
|
|
$res = $prod->fetch($line->fk_product); |
1390
|
|
|
if ($res > 0) { |
1391
|
|
|
if ($update_prices === true) { |
1392
|
|
|
$pu_ht = $prod->price; |
1393
|
|
|
$tva_tx = get_default_tva($mysoc, $objsoc, $prod->id); |
1394
|
|
|
$remise_percent = $objsoc->remise_percent; |
1395
|
|
|
|
1396
|
|
|
if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) { |
1397
|
|
|
$pu_ht = $prod->multiprices[$objsoc->price_level]; |
1398
|
|
|
if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility |
1399
|
|
|
if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) { |
1400
|
|
|
$tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level]; |
1401
|
|
|
} |
1402
|
|
|
} |
1403
|
|
|
} elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) { |
1404
|
|
|
$prodcustprice = new ProductCustomerPrice($this->db); |
1405
|
|
|
$filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id); |
1406
|
|
|
$result = $prodcustprice->fetchAll('', '', 0, 0, $filter); |
1407
|
|
|
if ($result) { |
1408
|
|
|
// If there is some prices specific to the customer |
1409
|
|
|
if (count($prodcustprice->lines) > 0) { |
1410
|
|
|
$pu_ht = price($prodcustprice->lines[0]->price); |
1411
|
|
|
$tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx . ' (' . $prodcustprice->lines[0]->default_vat_code . ' )' : $prodcustprice->lines[0]->tva_tx); |
1412
|
|
|
if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) { |
1413
|
|
|
$tva_tx .= ' (' . $prodcustprice->lines[0]->default_vat_code . ')'; |
1414
|
|
|
} |
1415
|
|
|
} |
1416
|
|
|
} |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
|
|
$line->subprice = $pu_ht; |
1420
|
|
|
$line->tva_tx = $tva_tx; |
1421
|
|
|
$line->remise_percent = $remise_percent; |
1422
|
|
|
} |
1423
|
|
|
if ($update_desc === true) { |
1424
|
|
|
$line->desc = $prod->description; |
1425
|
|
|
} |
1426
|
|
|
} |
1427
|
|
|
} |
1428
|
|
|
} |
1429
|
|
|
} |
1430
|
|
|
} |
1431
|
|
|
|
1432
|
|
|
$object->id = 0; |
1433
|
|
|
$object->ref = ''; |
1434
|
|
|
$object->entity = (!empty($forceentity) ? $forceentity : $object->entity); |
1435
|
|
|
$object->statut = self::STATUS_DRAFT; |
1436
|
|
|
|
1437
|
|
|
// Clear fields |
1438
|
|
|
$object->user_creation_id = $user->id; |
1439
|
|
|
$object->user_validation_id = 0; |
1440
|
|
|
$object->date = $now; |
1441
|
|
|
$object->datep = $now; // deprecated |
1442
|
|
|
$object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600); |
1443
|
|
|
if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) { |
1444
|
|
|
$object->ref_client = ''; |
1445
|
|
|
} |
1446
|
|
|
if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) { |
1447
|
|
|
$object->note_private = ''; |
1448
|
|
|
$object->note_public = ''; |
1449
|
|
|
} |
1450
|
|
|
// Create clone |
1451
|
|
|
$object->context['createfromclone'] = 'createfromclone'; |
1452
|
|
|
$result = $object->create($user); |
1453
|
|
|
if ($result < 0) { |
1454
|
|
|
$this->error = $object->error; |
1455
|
|
|
$this->errors = array_merge($this->errors, $object->errors); |
1456
|
|
|
$error++; |
1457
|
|
|
} |
1458
|
|
|
|
1459
|
|
|
if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) { |
1460
|
|
|
// copy internal contacts |
1461
|
|
|
if ($object->copy_linked_contact($this, 'internal') < 0) { |
1462
|
|
|
$error++; |
1463
|
|
|
} |
1464
|
|
|
} |
1465
|
|
|
|
1466
|
|
|
if (!$error) { |
1467
|
|
|
// copy external contacts if same company |
1468
|
|
|
if ($this->socid == $object->socid) { |
1469
|
|
|
if ($object->copy_linked_contact($this, 'external') < 0) { |
1470
|
|
|
$error++; |
1471
|
|
|
} |
1472
|
|
|
} |
1473
|
|
|
} |
1474
|
|
|
|
1475
|
|
|
if (!$error) { |
1476
|
|
|
// Hook of thirdparty module |
1477
|
|
|
if (is_object($hookmanager)) { |
1478
|
|
|
$parameters = array('objFrom' => $this, 'clonedObj' => $object); |
1479
|
|
|
$action = ''; |
1480
|
|
|
$reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks |
1481
|
|
|
if ($reshook < 0) { |
1482
|
|
|
$this->setErrorsFromObject($hookmanager); |
1483
|
|
|
$error++; |
1484
|
|
|
} |
1485
|
|
|
} |
1486
|
|
|
} |
1487
|
|
|
|
1488
|
|
|
unset($object->context['createfromclone']); |
1489
|
|
|
|
1490
|
|
|
// End |
1491
|
|
|
if (!$error) { |
1492
|
|
|
$this->db->commit(); |
1493
|
|
|
return $object->id; |
1494
|
|
|
} else { |
1495
|
|
|
$this->db->rollback(); |
1496
|
|
|
return -1; |
1497
|
|
|
} |
1498
|
|
|
} |
1499
|
|
|
|
1500
|
|
|
|
1501
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
1502
|
|
|
|
1503
|
|
|
/** |
1504
|
|
|
* Create commercial proposal into database |
1505
|
|
|
* this->ref can be set or empty. If empty, we will use "(PROVid)" |
1506
|
|
|
* |
1507
|
|
|
* @param User $user User that create |
1508
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
1509
|
|
|
* @return int Return integer <0 if KO, >=0 if OK |
1510
|
|
|
*/ |
1511
|
|
|
public function create($user, $notrigger = 0) |
1512
|
|
|
{ |
1513
|
|
|
global $conf, $hookmanager, $mysoc; |
1514
|
|
|
$error = 0; |
1515
|
|
|
|
1516
|
|
|
$now = dol_now(); |
1517
|
|
|
|
1518
|
|
|
// Clean parameters |
1519
|
|
|
if (empty($this->date)) { |
1520
|
|
|
$this->date = $this->datep; |
1521
|
|
|
} |
1522
|
|
|
$this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600); |
1523
|
|
|
if (empty($this->availability_id)) { |
1524
|
|
|
$this->availability_id = 0; |
1525
|
|
|
} |
1526
|
|
|
if (empty($this->demand_reason_id)) { |
1527
|
|
|
$this->demand_reason_id = 0; |
1528
|
|
|
} |
1529
|
|
|
|
1530
|
|
|
// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate) |
1531
|
|
|
if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) { |
1532
|
|
|
list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date); |
1533
|
|
|
} else { |
1534
|
|
|
$this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code); |
1535
|
|
|
} |
1536
|
|
|
if (empty($this->fk_multicurrency)) { |
1537
|
|
|
$this->multicurrency_code = $conf->currency; |
1538
|
|
|
$this->fk_multicurrency = 0; |
1539
|
|
|
$this->multicurrency_tx = 1; |
1540
|
|
|
} |
1541
|
|
|
|
1542
|
|
|
// Set tmp vars |
1543
|
|
|
$delivery_date = $this->delivery_date; |
1544
|
|
|
|
1545
|
|
|
dol_syslog(get_only_class($this) . "::create"); |
1546
|
|
|
|
1547
|
|
|
// Check parameters |
1548
|
|
|
$result = $this->fetch_thirdparty(); |
1549
|
|
|
if ($result < 0) { |
1550
|
|
|
$this->error = "Failed to fetch company"; |
1551
|
|
|
dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_ERR); |
1552
|
|
|
return -3; |
1553
|
|
|
} |
1554
|
|
|
|
1555
|
|
|
// Check parameters |
1556
|
|
|
if (!empty($this->ref)) { // We check that ref is not already used |
1557
|
|
|
$result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used |
1558
|
|
|
if ($result > 0) { |
1559
|
|
|
$this->error = 'ErrorRefAlreadyExists'; |
1560
|
|
|
dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_WARNING); |
1561
|
|
|
$this->db->rollback(); |
1562
|
|
|
return -1; |
1563
|
|
|
} |
1564
|
|
|
} |
1565
|
|
|
|
1566
|
|
|
if (empty($this->date)) { |
1567
|
|
|
$this->error = "Date of proposal is required"; |
1568
|
|
|
dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_ERR); |
1569
|
|
|
return -4; |
1570
|
|
|
} |
1571
|
|
|
|
1572
|
|
|
|
1573
|
|
|
$this->db->begin(); |
1574
|
|
|
|
1575
|
|
|
// Insert into database |
1576
|
|
|
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "propal ("; |
1577
|
|
|
$sql .= "fk_soc"; |
1578
|
|
|
$sql .= ", price"; |
1579
|
|
|
$sql .= ", total_tva"; |
1580
|
|
|
$sql .= ", total_ttc"; |
1581
|
|
|
$sql .= ", datep"; |
1582
|
|
|
$sql .= ", datec"; |
1583
|
|
|
$sql .= ", ref"; |
1584
|
|
|
$sql .= ", fk_user_author"; |
1585
|
|
|
$sql .= ", note_private"; |
1586
|
|
|
$sql .= ", note_public"; |
1587
|
|
|
$sql .= ", model_pdf"; |
1588
|
|
|
$sql .= ", fin_validite"; |
1589
|
|
|
$sql .= ", fk_cond_reglement"; |
1590
|
|
|
$sql .= ", deposit_percent"; |
1591
|
|
|
$sql .= ", fk_mode_reglement"; |
1592
|
|
|
$sql .= ", fk_account"; |
1593
|
|
|
$sql .= ", ref_client"; |
1594
|
|
|
$sql .= ", ref_ext"; |
1595
|
|
|
$sql .= ", date_livraison"; |
1596
|
|
|
$sql .= ", fk_shipping_method"; |
1597
|
|
|
$sql .= ", fk_warehouse"; |
1598
|
|
|
$sql .= ", fk_availability"; |
1599
|
|
|
$sql .= ", fk_input_reason"; |
1600
|
|
|
$sql .= ", fk_projet"; |
1601
|
|
|
$sql .= ", fk_incoterms"; |
1602
|
|
|
$sql .= ", location_incoterms"; |
1603
|
|
|
$sql .= ", entity"; |
1604
|
|
|
$sql .= ", fk_multicurrency"; |
1605
|
|
|
$sql .= ", multicurrency_code"; |
1606
|
|
|
$sql .= ", multicurrency_tx"; |
1607
|
|
|
$sql .= ") "; |
1608
|
|
|
$sql .= " VALUES ("; |
1609
|
|
|
$sql .= $this->socid; |
1610
|
|
|
$sql .= ", 0"; |
1611
|
|
|
$sql .= ", 0"; |
1612
|
|
|
$sql .= ", 0"; |
1613
|
|
|
$sql .= ", '" . $this->db->idate($this->date) . "'"; |
1614
|
|
|
$sql .= ", '" . $this->db->idate($now) . "'"; |
1615
|
|
|
$sql .= ", '(PROV)'"; |
1616
|
|
|
$sql .= ", " . ($user->id > 0 ? ((int)$user->id) : "NULL"); |
1617
|
|
|
$sql .= ", '" . $this->db->escape($this->note_private) . "'"; |
1618
|
|
|
$sql .= ", '" . $this->db->escape($this->note_public) . "'"; |
1619
|
|
|
$sql .= ", '" . $this->db->escape($this->model_pdf) . "'"; |
1620
|
|
|
$sql .= ", " . ($this->fin_validite != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : "NULL"); |
1621
|
|
|
$sql .= ", " . ($this->cond_reglement_id > 0 ? ((int)$this->cond_reglement_id) : 'NULL'); |
1622
|
|
|
$sql .= ", " . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL'); |
1623
|
|
|
$sql .= ", " . ($this->mode_reglement_id > 0 ? ((int)$this->mode_reglement_id) : 'NULL'); |
1624
|
|
|
$sql .= ", " . ($this->fk_account > 0 ? ((int)$this->fk_account) : 'NULL'); |
1625
|
|
|
$sql .= ", '" . $this->db->escape($this->ref_client) . "'"; |
1626
|
|
|
$sql .= ", '" . $this->db->escape($this->ref_ext) . "'"; |
1627
|
|
|
$sql .= ", " . (empty($delivery_date) ? "NULL" : "'" . $this->db->idate($delivery_date) . "'"); |
1628
|
|
|
$sql .= ", " . ($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL'); |
1629
|
|
|
$sql .= ", " . ($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL'); |
1630
|
|
|
$sql .= ", " . $this->availability_id; |
1631
|
|
|
$sql .= ", " . $this->demand_reason_id; |
1632
|
|
|
$sql .= ", " . ($this->fk_project ? $this->fk_project : "null"); |
1633
|
|
|
$sql .= ", " . (int)$this->fk_incoterms; |
1634
|
|
|
$sql .= ", '" . $this->db->escape($this->location_incoterms) . "'"; |
1635
|
|
|
$sql .= ", " . setEntity($this); |
1636
|
|
|
$sql .= ", " . (int)$this->fk_multicurrency; |
1637
|
|
|
$sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'"; |
1638
|
|
|
$sql .= ", " . (float)$this->multicurrency_tx; |
1639
|
|
|
$sql .= ")"; |
1640
|
|
|
|
1641
|
|
|
dol_syslog(get_only_class($this) . "::create", LOG_DEBUG); |
1642
|
|
|
$resql = $this->db->query($sql); |
1643
|
|
|
if ($resql) { |
1644
|
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "propal"); |
1645
|
|
|
|
1646
|
|
|
if ($this->id) { |
1647
|
|
|
$this->ref = '(PROV' . $this->id . ')'; |
1648
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . "propal SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int)$this->id); |
1649
|
|
|
|
1650
|
|
|
dol_syslog(get_only_class($this) . "::create", LOG_DEBUG); |
1651
|
|
|
$resql = $this->db->query($sql); |
1652
|
|
|
if (!$resql) { |
1653
|
|
|
$error++; |
1654
|
|
|
} |
1655
|
|
|
|
1656
|
|
|
if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects |
1657
|
|
|
$this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds |
1658
|
|
|
} |
1659
|
|
|
|
1660
|
|
|
// Add object linked |
1661
|
|
|
if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) { |
1662
|
|
|
foreach ($this->linked_objects as $origin => $tmp_origin_id) { |
1663
|
|
|
if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...)) |
1664
|
|
|
foreach ($tmp_origin_id as $origin_id) { |
1665
|
|
|
$ret = $this->add_object_linked($origin, $origin_id); |
1666
|
|
|
if (!$ret) { |
1667
|
|
|
$this->error = $this->db->lasterror(); |
1668
|
|
|
$error++; |
1669
|
|
|
} |
1670
|
|
|
} |
1671
|
|
|
} else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1)) |
1672
|
|
|
$origin_id = $tmp_origin_id; |
1673
|
|
|
$ret = $this->add_object_linked($origin, $origin_id); |
1674
|
|
|
if (!$ret) { |
1675
|
|
|
$this->error = $this->db->lasterror(); |
1676
|
|
|
$error++; |
1677
|
|
|
} |
1678
|
|
|
} |
1679
|
|
|
} |
1680
|
|
|
} |
1681
|
|
|
|
1682
|
|
|
/* |
1683
|
|
|
* Insertion du detail des produits dans la base |
1684
|
|
|
* Insert products detail in database |
1685
|
|
|
*/ |
1686
|
|
|
if (!$error) { |
1687
|
|
|
$fk_parent_line = 0; |
1688
|
|
|
$num = count($this->lines); |
1689
|
|
|
|
1690
|
|
|
for ($i = 0; $i < $num; $i++) { |
1691
|
|
|
if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API |
1692
|
|
|
// Convert into object this->lines[$i]. |
1693
|
|
|
$line = (object)$this->lines[$i]; |
1694
|
|
|
} else { |
1695
|
|
|
$line = $this->lines[$i]; |
1696
|
|
|
} |
1697
|
|
|
// Reset fk_parent_line for line that are not child lines or special product |
1698
|
|
|
if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) { |
1699
|
|
|
$fk_parent_line = 0; |
1700
|
|
|
} |
1701
|
|
|
// Complete vat rate with code |
1702
|
|
|
$vatrate = $line->tva_tx; |
1703
|
|
|
if ($line->vat_src_code && !preg_match('/\(.*\)/', $vatrate)) { |
1704
|
|
|
$vatrate .= ' (' . $line->vat_src_code . ')'; |
1705
|
|
|
} |
1706
|
|
|
|
1707
|
|
|
if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) { |
1708
|
|
|
$originid = $line->origin_id; |
1709
|
|
|
$origintype = $line->origin; |
1710
|
|
|
} else { |
1711
|
|
|
$originid = $line->id; |
1712
|
|
|
$origintype = $this->element; |
1713
|
|
|
} |
1714
|
|
|
|
1715
|
|
|
$result = $this->addline( |
1716
|
|
|
$line->desc, |
1717
|
|
|
$line->subprice, |
1718
|
|
|
$line->qty, |
1719
|
|
|
$vatrate, |
1720
|
|
|
$line->localtax1_tx, |
1721
|
|
|
$line->localtax2_tx, |
1722
|
|
|
$line->fk_product, |
1723
|
|
|
$line->remise_percent, |
1724
|
|
|
'HT', |
1725
|
|
|
0, |
1726
|
|
|
$line->info_bits, |
1727
|
|
|
$line->product_type, |
1728
|
|
|
$line->rang, |
1729
|
|
|
$line->special_code, |
1730
|
|
|
$fk_parent_line, |
1731
|
|
|
$line->fk_fournprice, |
1732
|
|
|
$line->pa_ht, |
1733
|
|
|
$line->label, |
1734
|
|
|
$line->date_start, |
1735
|
|
|
$line->date_end, |
1736
|
|
|
$line->array_options, |
1737
|
|
|
$line->fk_unit, |
1738
|
|
|
$origintype, |
1739
|
|
|
$originid, |
1740
|
|
|
0, |
1741
|
|
|
0, |
1742
|
|
|
1 |
1743
|
|
|
); |
1744
|
|
|
|
1745
|
|
|
if ($result < 0) { |
1746
|
|
|
$error++; |
1747
|
|
|
$this->error = $this->db->error; |
1748
|
|
|
dol_print_error($this->db); |
1749
|
|
|
break; |
1750
|
|
|
} |
1751
|
|
|
|
1752
|
|
|
// Set the id on created row |
1753
|
|
|
$line->id = $result; |
1754
|
|
|
|
1755
|
|
|
// Defined the new fk_parent_line |
1756
|
|
|
if ($result > 0 && $line->product_type == 9) { |
1757
|
|
|
$fk_parent_line = $result; |
1758
|
|
|
} |
1759
|
|
|
} |
1760
|
|
|
} |
1761
|
|
|
|
1762
|
|
|
// Set delivery address |
1763
|
|
|
/*if (! $error && $this->fk_delivery_address) |
1764
|
|
|
{ |
1765
|
|
|
$sql = "UPDATE ".MAIN_DB_PREFIX."propal"; |
1766
|
|
|
$sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address); |
1767
|
|
|
$sql.= " WHERE ref = '".$this->db->escape($this->ref)."'"; |
1768
|
|
|
$sql.= " AND entity = ".setEntity($this); |
1769
|
|
|
|
1770
|
|
|
$result=$this->db->query($sql); |
1771
|
|
|
}*/ |
1772
|
|
|
|
1773
|
|
|
if (!$error) { |
1774
|
|
|
// Mise a jour infos denormalisees |
1775
|
|
|
$resql = $this->update_price(1, 'auto', 0, $mysoc); |
1776
|
|
|
if ($resql) { |
1777
|
|
|
$action = 'update'; |
1778
|
|
|
|
1779
|
|
|
// Actions on extra fields |
1780
|
|
|
if (!$error) { |
1781
|
|
|
$result = $this->insertExtraFields(); |
1782
|
|
|
if ($result < 0) { |
1783
|
|
|
$error++; |
1784
|
|
|
} |
1785
|
|
|
} |
1786
|
|
|
|
1787
|
|
|
if (!$error && !$notrigger) { |
1788
|
|
|
// Call trigger |
1789
|
|
|
$result = $this->call_trigger('PROPAL_CREATE', $user); |
1790
|
|
|
if ($result < 0) { |
1791
|
|
|
$error++; |
1792
|
|
|
} |
1793
|
|
|
// End call triggers |
1794
|
|
|
} |
1795
|
|
|
} else { |
1796
|
|
|
$this->error = $this->db->lasterror(); |
1797
|
|
|
$error++; |
1798
|
|
|
} |
1799
|
|
|
} |
1800
|
|
|
} else { |
1801
|
|
|
$this->error = $this->db->lasterror(); |
1802
|
|
|
$error++; |
1803
|
|
|
} |
1804
|
|
|
|
1805
|
|
|
if (!$error) { |
1806
|
|
|
$this->db->commit(); |
1807
|
|
|
dol_syslog(get_only_class($this) . "::create done id=" . $this->id); |
1808
|
|
|
return $this->id; |
1809
|
|
|
} else { |
1810
|
|
|
$this->db->rollback(); |
1811
|
|
|
return -2; |
1812
|
|
|
} |
1813
|
|
|
} else { |
1814
|
|
|
$this->error = $this->db->lasterror(); |
1815
|
|
|
$this->db->rollback(); |
1816
|
|
|
return -1; |
1817
|
|
|
} |
1818
|
|
|
} |
1819
|
|
|
|
1820
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
1821
|
|
|
|
1822
|
|
|
/** |
1823
|
|
|
* Add a proposal line into database (linked to product/service or not) |
1824
|
|
|
* The parameters are already supposed to be appropriate and with final values to the call |
1825
|
|
|
* of this method. Also, for the VAT rate, it must have already been defined |
1826
|
|
|
* by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product) |
1827
|
|
|
* and desc must already have the right value (it's up to the caller to manage multilanguage) |
1828
|
|
|
* |
1829
|
|
|
* @param string $desc Description of line |
1830
|
|
|
* @param float $pu_ht Unit price |
1831
|
|
|
* @param float $qty Quantity |
1832
|
|
|
* @param float|string $txtva Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)') |
1833
|
|
|
* @param float $txlocaltax1 Local tax 1 rate (deprecated, use instead txtva with code inside) |
1834
|
|
|
* @param float $txlocaltax2 Local tax 2 rate (deprecated, use instead txtva with code inside) |
1835
|
|
|
* @param int $fk_product Product/Service ID predefined |
1836
|
|
|
* @param float $remise_percent Pourcentage de remise de la ligne |
1837
|
|
|
* @param string $price_base_type HT or TTC |
1838
|
|
|
* @param float $pu_ttc Prix unitaire TTC |
1839
|
|
|
* @param int $info_bits Bits for type of lines |
1840
|
|
|
* @param int $type Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used. |
1841
|
|
|
* @param int $rang Position of line |
1842
|
|
|
* @param int $special_code Special code (also used by externals modules!) |
1843
|
|
|
* @param int $fk_parent_line Id of parent line |
1844
|
|
|
* @param int $fk_fournprice Id supplier price |
1845
|
|
|
* @param int $pa_ht Buying price without tax |
1846
|
|
|
* @param string $label ??? |
1847
|
|
|
* @param int|string $date_start Start date of the line |
1848
|
|
|
* @param int|string $date_end End date of the line |
1849
|
|
|
* @param array $array_options extrafields array |
1850
|
|
|
* @param int|null $fk_unit Code of the unit to use. Null to use the default one |
1851
|
|
|
* @param string $origin Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be 'orderdet', 'propaldet'..., else 'order','propal,'.... |
1852
|
|
|
* @param int $origin_id Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be Id of origin object (aka line id), else object id |
1853
|
|
|
* @param double $pu_ht_devise Unit price in currency |
1854
|
|
|
* @param int $fk_remise_except Id discount if line is from a discount |
1855
|
|
|
* @param int $noupdateafterinsertline No update after insert of line |
1856
|
|
|
* @return int >0 if OK, <0 if KO |
1857
|
|
|
* @see add_product() |
1858
|
|
|
*/ |
1859
|
|
|
public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $date_start = '', $date_end = '', $array_options = array(), $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $fk_remise_except = 0, $noupdateafterinsertline = 0) |
1860
|
|
|
{ |
1861
|
|
|
global $mysoc, $conf, $langs; |
1862
|
|
|
|
1863
|
|
|
dol_syslog(get_only_class($this) . "::addline propalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type, fk_remise_except=" . $fk_remise_except); |
1864
|
|
|
|
1865
|
|
|
if ($this->statut == self::STATUS_DRAFT) { |
1866
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
1867
|
|
|
|
1868
|
|
|
// Clean parameters |
1869
|
|
|
if (empty($remise_percent)) { |
1870
|
|
|
$remise_percent = 0; |
1871
|
|
|
} |
1872
|
|
|
if (empty($qty)) { |
1873
|
|
|
$qty = 0; |
1874
|
|
|
} |
1875
|
|
|
if (empty($info_bits)) { |
1876
|
|
|
$info_bits = 0; |
1877
|
|
|
} |
1878
|
|
|
if (empty($rang)) { |
1879
|
|
|
$rang = 0; |
1880
|
|
|
} |
1881
|
|
|
if (empty($fk_parent_line) || $fk_parent_line < 0) { |
1882
|
|
|
$fk_parent_line = 0; |
1883
|
|
|
} |
1884
|
|
|
|
1885
|
|
|
$remise_percent = price2num($remise_percent); |
1886
|
|
|
$qty = (float)price2num($qty); |
1887
|
|
|
$pu_ht = price2num($pu_ht); |
1888
|
|
|
$pu_ht_devise = price2num($pu_ht_devise); |
1889
|
|
|
$pu_ttc = price2num($pu_ttc); |
1890
|
|
|
if (!preg_match('/\((.*)\)/', (string)$txtva)) { |
1891
|
|
|
$txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1' |
1892
|
|
|
} |
1893
|
|
|
$txlocaltax1 = price2num($txlocaltax1); |
1894
|
|
|
$txlocaltax2 = price2num($txlocaltax2); |
1895
|
|
|
$pa_ht = price2num($pa_ht); |
1896
|
|
|
if ($price_base_type == 'HT') { |
1897
|
|
|
$pu = $pu_ht; |
1898
|
|
|
} else { |
1899
|
|
|
$pu = $pu_ttc; |
1900
|
|
|
} |
1901
|
|
|
|
1902
|
|
|
// Check parameters |
1903
|
|
|
if ($type < 0) { |
1904
|
|
|
return -1; |
1905
|
|
|
} |
1906
|
|
|
|
1907
|
|
|
if ($date_start && $date_end && $date_start > $date_end) { |
1908
|
|
|
$langs->load("errors"); |
1909
|
|
|
$this->error = $langs->trans('ErrorStartDateGreaterEnd'); |
1910
|
|
|
return -1; |
1911
|
|
|
} |
1912
|
|
|
|
1913
|
|
|
$this->db->begin(); |
1914
|
|
|
|
1915
|
|
|
$product_type = $type; |
1916
|
|
|
if (!empty($fk_product) && $fk_product > 0) { |
1917
|
|
|
$product = new Product($this->db); |
1918
|
|
|
$result = $product->fetch($fk_product); |
1919
|
|
|
$product_type = $product->type; |
1920
|
|
|
|
1921
|
|
|
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) { |
1922
|
|
|
$langs->load("errors"); |
1923
|
|
|
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref); |
1924
|
|
|
$this->db->rollback(); |
1925
|
|
|
return -3; |
1926
|
|
|
} |
1927
|
|
|
} |
1928
|
|
|
|
1929
|
|
|
// Calcul du total TTC et de la TVA pour la ligne a partir de |
1930
|
|
|
// qty, pu, remise_percent et txtva |
1931
|
|
|
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker |
1932
|
|
|
// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. |
1933
|
|
|
|
1934
|
|
|
$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc); |
1935
|
|
|
|
1936
|
|
|
// Clean vat code |
1937
|
|
|
$reg = array(); |
1938
|
|
|
$vat_src_code = ''; |
1939
|
|
|
if (preg_match('/\((.*)\)/', $txtva, $reg)) { |
1940
|
|
|
$vat_src_code = $reg[1]; |
1941
|
|
|
$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate. |
1942
|
|
|
} |
1943
|
|
|
|
1944
|
|
|
$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise); |
1945
|
|
|
|
1946
|
|
|
$total_ht = $tabprice[0]; |
1947
|
|
|
$total_tva = $tabprice[1]; |
1948
|
|
|
$total_ttc = $tabprice[2]; |
1949
|
|
|
$total_localtax1 = $tabprice[9]; |
1950
|
|
|
$total_localtax2 = $tabprice[10]; |
1951
|
|
|
$pu_ht = $tabprice[3]; |
1952
|
|
|
$pu_tva = $tabprice[4]; |
1953
|
|
|
$pu_ttc = $tabprice[5]; |
1954
|
|
|
|
1955
|
|
|
// MultiCurrency |
1956
|
|
|
$multicurrency_total_ht = $tabprice[16]; |
1957
|
|
|
$multicurrency_total_tva = $tabprice[17]; |
1958
|
|
|
$multicurrency_total_ttc = $tabprice[18]; |
1959
|
|
|
$pu_ht_devise = $tabprice[19]; |
1960
|
|
|
|
1961
|
|
|
// Rang to use |
1962
|
|
|
$ranktouse = $rang; |
1963
|
|
|
if ($ranktouse == -1) { |
1964
|
|
|
$rangmax = $this->line_max($fk_parent_line); |
1965
|
|
|
$ranktouse = $rangmax + 1; |
1966
|
|
|
} |
1967
|
|
|
|
1968
|
|
|
// TODO A virer |
1969
|
|
|
// Anciens indicateurs: $price, $remise (a ne plus utiliser) |
1970
|
|
|
$price = $pu; |
1971
|
|
|
$remise = 0; |
1972
|
|
|
if ((float)$remise_percent > 0) { |
1973
|
|
|
$remise = round(((float)$pu * (float)$remise_percent / 100), 2); |
1974
|
|
|
$price = (float)$pu - $remise; |
1975
|
|
|
} |
1976
|
|
|
|
1977
|
|
|
// Insert line |
1978
|
|
|
$this->line = new PropaleLigne($this->db); |
1979
|
|
|
|
1980
|
|
|
$this->line->context = $this->context; |
1981
|
|
|
|
1982
|
|
|
$this->line->fk_propal = $this->id; |
1983
|
|
|
$this->line->label = $label; |
1984
|
|
|
$this->line->desc = $desc; |
1985
|
|
|
$this->line->qty = $qty; |
1986
|
|
|
|
1987
|
|
|
$this->line->vat_src_code = $vat_src_code; |
1988
|
|
|
$this->line->tva_tx = $txtva; |
1989
|
|
|
$this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0); |
1990
|
|
|
$this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0); |
1991
|
|
|
$this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0]; |
1992
|
|
|
$this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2]; |
1993
|
|
|
$this->line->fk_product = $fk_product; |
1994
|
|
|
$this->line->product_type = $type; |
1995
|
|
|
$this->line->fk_remise_except = $fk_remise_except; |
1996
|
|
|
$this->line->remise_percent = $remise_percent; |
1997
|
|
|
$this->line->subprice = $pu_ht; |
1998
|
|
|
$this->line->rang = $ranktouse; |
1999
|
|
|
$this->line->info_bits = $info_bits; |
2000
|
|
|
$this->line->total_ht = $total_ht; |
2001
|
|
|
$this->line->total_tva = $total_tva; |
2002
|
|
|
$this->line->total_localtax1 = $total_localtax1; |
2003
|
|
|
$this->line->total_localtax2 = $total_localtax2; |
2004
|
|
|
$this->line->total_ttc = $total_ttc; |
2005
|
|
|
$this->line->special_code = $special_code; |
2006
|
|
|
$this->line->fk_parent_line = $fk_parent_line; |
2007
|
|
|
$this->line->fk_unit = $fk_unit; |
2008
|
|
|
|
2009
|
|
|
$this->line->date_start = $date_start; |
2010
|
|
|
$this->line->date_end = $date_end; |
2011
|
|
|
|
2012
|
|
|
$this->line->fk_fournprice = $fk_fournprice; |
2013
|
|
|
$this->line->pa_ht = $pa_ht; |
2014
|
|
|
|
2015
|
|
|
$this->line->origin_id = $origin_id; |
2016
|
|
|
$this->line->origin = $origin; |
|
|
|
|
2017
|
|
|
|
2018
|
|
|
// Multicurrency |
2019
|
|
|
$this->line->fk_multicurrency = $this->fk_multicurrency; |
2020
|
|
|
$this->line->multicurrency_code = $this->multicurrency_code; |
2021
|
|
|
$this->line->multicurrency_subprice = $pu_ht_devise; |
2022
|
|
|
$this->line->multicurrency_total_ht = $multicurrency_total_ht; |
2023
|
|
|
$this->line->multicurrency_total_tva = $multicurrency_total_tva; |
2024
|
|
|
$this->line->multicurrency_total_ttc = $multicurrency_total_ttc; |
2025
|
|
|
|
2026
|
|
|
// Mise en option de la ligne |
2027
|
|
|
if (empty($qty) && empty($special_code)) { |
2028
|
|
|
$this->line->special_code = 3; |
2029
|
|
|
} |
2030
|
|
|
|
2031
|
|
|
// TODO deprecated |
2032
|
|
|
$this->line->price = $price; |
2033
|
|
|
|
2034
|
|
|
if (is_array($array_options) && count($array_options) > 0) { |
2035
|
|
|
$this->line->array_options = $array_options; |
2036
|
|
|
} |
2037
|
|
|
|
2038
|
|
|
$result = $this->line->insert(); |
2039
|
|
|
if ($result > 0) { |
2040
|
|
|
// Reorder if child line |
2041
|
|
|
if (!empty($fk_parent_line)) { |
2042
|
|
|
$this->line_order(true, 'DESC'); |
2043
|
|
|
} elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines |
2044
|
|
|
$linecount = count($this->lines); |
2045
|
|
|
for ($ii = $ranktouse; $ii <= $linecount; $ii++) { |
2046
|
|
|
$this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1); |
2047
|
|
|
} |
2048
|
|
|
} |
2049
|
|
|
|
2050
|
|
|
// Mise a jour information denormalisees au niveau de la propale meme |
2051
|
|
|
if (empty($noupdateafterinsertline)) { |
2052
|
|
|
$result = $this->update_price(1, 'auto', 0, $mysoc); // This method is designed to add line from user input so total calculation must be done using 'auto' mode. |
2053
|
|
|
} |
2054
|
|
|
|
2055
|
|
|
if ($result > 0) { |
2056
|
|
|
$this->db->commit(); |
2057
|
|
|
return $this->line->id; |
2058
|
|
|
} else { |
2059
|
|
|
$this->error = $this->db->error(); |
2060
|
|
|
$this->db->rollback(); |
2061
|
|
|
return -1; |
2062
|
|
|
} |
2063
|
|
|
} else { |
2064
|
|
|
$this->error = $this->line->error; |
2065
|
|
|
$this->errors = $this->line->errors; |
2066
|
|
|
$this->db->rollback(); |
2067
|
|
|
return -2; |
2068
|
|
|
} |
2069
|
|
|
} else { |
2070
|
|
|
dol_syslog(get_only_class($this) . "::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR); |
2071
|
|
|
return -3; |
2072
|
|
|
} |
2073
|
|
|
} |
2074
|
|
|
|
2075
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2076
|
|
|
|
2077
|
|
|
/** |
2078
|
|
|
* Set status to validated |
2079
|
|
|
* |
2080
|
|
|
* @param User $user Object user that validate |
2081
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0=execute triggers |
2082
|
|
|
* @return int Return integer <0 if KO, 0=Nothing done, >=0 if OK |
2083
|
|
|
*/ |
2084
|
|
|
public function valid($user, $notrigger = 0) |
2085
|
|
|
{ |
2086
|
|
|
global $conf; |
2087
|
|
|
|
2088
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
2089
|
|
|
|
2090
|
|
|
$error = 0; |
2091
|
|
|
|
2092
|
|
|
// Protection |
2093
|
|
|
if ($this->statut == self::STATUS_VALIDATED) { |
2094
|
|
|
dol_syslog(get_only_class($this) . "::valid action abandoned: already validated", LOG_WARNING); |
2095
|
|
|
return 0; |
2096
|
|
|
} |
2097
|
|
|
|
2098
|
|
|
if ( |
2099
|
|
|
!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer')) |
2100
|
|
|
|| (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate'))) |
2101
|
|
|
) { |
2102
|
|
|
$this->error = 'ErrorPermissionDenied'; |
2103
|
|
|
dol_syslog(get_only_class($this) . "::valid " . $this->error, LOG_ERR); |
2104
|
|
|
return -1; |
2105
|
|
|
} |
2106
|
|
|
|
2107
|
|
|
$now = dol_now(); |
2108
|
|
|
|
2109
|
|
|
$this->db->begin(); |
2110
|
|
|
|
2111
|
|
|
// Numbering module definition |
2112
|
|
|
$soc = new Societe($this->db); |
2113
|
|
|
$soc->fetch($this->socid); |
2114
|
|
|
|
2115
|
|
|
// Define new ref |
2116
|
|
|
if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life |
2117
|
|
|
$num = $this->getNextNumRef($soc); |
2118
|
|
|
} else { |
2119
|
|
|
$num = $this->ref; |
2120
|
|
|
} |
2121
|
|
|
$this->newref = dol_sanitizeFileName($num); |
2122
|
|
|
|
2123
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
2124
|
|
|
$sql .= " SET ref = '" . $this->db->escape($num) . "',"; |
2125
|
|
|
$sql .= " fk_statut = " . self::STATUS_VALIDATED . ", date_valid='" . $this->db->idate($now) . "', fk_user_valid=" . ((int)$user->id); |
2126
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id) . " AND fk_statut = " . self::STATUS_DRAFT; |
2127
|
|
|
|
2128
|
|
|
dol_syslog(get_only_class($this) . "::valid", LOG_DEBUG); |
2129
|
|
|
$resql = $this->db->query($sql); |
2130
|
|
|
if (!$resql) { |
2131
|
|
|
dol_print_error($this->db); |
2132
|
|
|
$error++; |
2133
|
|
|
} |
2134
|
|
|
|
2135
|
|
|
// Trigger calls |
2136
|
|
|
if (!$error && !$notrigger) { |
2137
|
|
|
// Call trigger |
2138
|
|
|
$result = $this->call_trigger('PROPAL_VALIDATE', $user); |
2139
|
|
|
if ($result < 0) { |
2140
|
|
|
$error++; |
2141
|
|
|
} |
2142
|
|
|
// End call triggers |
2143
|
|
|
} |
2144
|
|
|
|
2145
|
|
|
if (!$error) { |
2146
|
|
|
$this->oldref = $this->ref; |
2147
|
|
|
|
2148
|
|
|
// Rename directory if dir was a temporary ref |
2149
|
|
|
if (preg_match('/^[\(]?PROV/i', $this->ref)) { |
2150
|
|
|
// Now we rename also files into index |
2151
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'propale/" . $this->db->escape($this->newref) . "'"; |
2152
|
|
|
$sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . ((int)$conf->entity); |
2153
|
|
|
$resql = $this->db->query($sql); |
2154
|
|
|
if (!$resql) { |
2155
|
|
|
$error++; |
2156
|
|
|
$this->error = $this->db->lasterror(); |
2157
|
|
|
} |
2158
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'propale/" . $this->db->escape($this->newref) . "'"; |
2159
|
|
|
$sql .= " WHERE filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity; |
2160
|
|
|
$resql = $this->db->query($sql); |
2161
|
|
|
if (!$resql) { |
2162
|
|
|
$error++; |
2163
|
|
|
$this->error = $this->db->lasterror(); |
2164
|
|
|
} |
2165
|
|
|
|
2166
|
|
|
// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments |
2167
|
|
|
$oldref = dol_sanitizeFileName($this->ref); |
2168
|
|
|
$newref = dol_sanitizeFileName($num); |
2169
|
|
|
$dirsource = $conf->propal->multidir_output[$this->entity] . '/' . $oldref; |
2170
|
|
|
$dirdest = $conf->propal->multidir_output[$this->entity] . '/' . $newref; |
2171
|
|
|
if (!$error && file_exists($dirsource)) { |
2172
|
|
|
dol_syslog(get_only_class($this) . "::validate rename dir " . $dirsource . " into " . $dirdest); |
2173
|
|
|
if (@rename($dirsource, $dirdest)) { |
2174
|
|
|
dol_syslog("Rename ok"); |
2175
|
|
|
// Rename docs starting with $oldref with $newref |
2176
|
|
|
$listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/')); |
2177
|
|
|
foreach ($listoffiles as $fileentry) { |
2178
|
|
|
$dirsource = $fileentry['name']; |
2179
|
|
|
$dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource); |
2180
|
|
|
$dirsource = $fileentry['path'] . '/' . $dirsource; |
2181
|
|
|
$dirdest = $fileentry['path'] . '/' . $dirdest; |
2182
|
|
|
@rename($dirsource, $dirdest); |
|
|
|
|
2183
|
|
|
} |
2184
|
|
|
} |
2185
|
|
|
} |
2186
|
|
|
} |
2187
|
|
|
|
2188
|
|
|
$this->ref = $num; |
2189
|
|
|
$this->statut = self::STATUS_VALIDATED; |
2190
|
|
|
$this->status = self::STATUS_VALIDATED; |
2191
|
|
|
$this->user_validation_id = $user->id; |
2192
|
|
|
$this->datev = $now; |
2193
|
|
|
$this->date_validation = $now; |
2194
|
|
|
|
2195
|
|
|
$this->db->commit(); |
2196
|
|
|
return 1; |
2197
|
|
|
} else { |
2198
|
|
|
$this->db->rollback(); |
2199
|
|
|
return -1; |
2200
|
|
|
} |
2201
|
|
|
} |
2202
|
|
|
|
2203
|
|
|
/** |
2204
|
|
|
* Returns the reference to the following non used Proposal used depending on the active numbering module |
2205
|
|
|
* defined into PROPALE_ADDON |
2206
|
|
|
* |
2207
|
|
|
* @param Societe $soc Object thirdparty |
2208
|
|
|
* @return string Reference libre pour la propale |
2209
|
|
|
*/ |
2210
|
|
|
public function getNextNumRef($soc) |
2211
|
|
|
{ |
2212
|
|
|
global $conf, $langs; |
2213
|
|
|
$langs->load("propal"); |
2214
|
|
|
|
2215
|
|
|
$classname = getDolGlobalString('PROPALE_ADDON'); |
2216
|
|
|
|
2217
|
|
|
if (!empty($classname)) { |
2218
|
|
|
$mybool = false; |
2219
|
|
|
|
2220
|
|
|
$file = $classname . ".php"; |
2221
|
|
|
|
2222
|
|
|
// Include file with class |
2223
|
|
|
$dirmodels = array_merge(array('/'), (array)$conf->modules_parts['models']); |
2224
|
|
|
foreach ($dirmodels as $reldir) { |
2225
|
|
|
$dir = dol_buildpath($reldir . "core/modules/propale/"); |
2226
|
|
|
|
2227
|
|
|
// Load file with numbering class (if found) |
2228
|
|
|
$mybool = ((bool)@include_once $dir . $file) || $mybool; |
2229
|
|
|
} |
2230
|
|
|
|
2231
|
|
|
if (!$mybool) { |
2232
|
|
|
dol_print_error(null, "Failed to include file " . $file); |
2233
|
|
|
return ''; |
2234
|
|
|
} |
2235
|
|
|
|
2236
|
|
|
$obj = new $classname(); |
2237
|
|
|
$numref = ""; |
2238
|
|
|
$numref = $obj->getNextValue($soc, $this); |
2239
|
|
|
|
2240
|
|
|
if ($numref != "") { |
2241
|
|
|
return $numref; |
2242
|
|
|
} else { |
2243
|
|
|
$this->error = $obj->error; |
2244
|
|
|
//dol_print_error($db,"Propale::getNextNumRef ".$obj->error); |
2245
|
|
|
return ""; |
2246
|
|
|
} |
2247
|
|
|
} else { |
2248
|
|
|
$langs->load("errors"); |
2249
|
|
|
print $langs->trans("Error") . " " . $langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal")); |
2250
|
|
|
return ""; |
2251
|
|
|
} |
2252
|
|
|
} |
2253
|
|
|
|
2254
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2255
|
|
|
|
2256
|
|
|
/** |
2257
|
|
|
* Define proposal date |
2258
|
|
|
* |
2259
|
|
|
* @param User $user Object user that modify |
2260
|
|
|
* @param int $date Date |
2261
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2262
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2263
|
|
|
*/ |
2264
|
|
|
public function set_date($user, $date, $notrigger = 0) |
2265
|
|
|
{ |
2266
|
|
|
// phpcs:enable |
2267
|
|
|
if (empty($date)) { |
2268
|
|
|
$this->error = 'ErrorBadParameter'; |
2269
|
|
|
dol_syslog(get_only_class($this) . "::set_date " . $this->error, LOG_ERR); |
2270
|
|
|
return -1; |
2271
|
|
|
} |
2272
|
|
|
|
2273
|
|
|
if ($user->hasRight('propal', 'creer')) { |
2274
|
|
|
$error = 0; |
2275
|
|
|
|
2276
|
|
|
$this->db->begin(); |
2277
|
|
|
|
2278
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET datep = '" . $this->db->idate($date) . "'"; |
2279
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2280
|
|
|
|
2281
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
2282
|
|
|
$resql = $this->db->query($sql); |
2283
|
|
|
if (!$resql) { |
2284
|
|
|
$this->errors[] = $this->db->error(); |
2285
|
|
|
$error++; |
2286
|
|
|
} |
2287
|
|
|
|
2288
|
|
|
if (!$error) { |
2289
|
|
|
$this->oldcopy = clone $this; |
2290
|
|
|
$this->date = $date; |
2291
|
|
|
$this->datep = $date; // deprecated |
2292
|
|
|
} |
2293
|
|
|
|
2294
|
|
|
if (!$notrigger && empty($error)) { |
2295
|
|
|
// Call trigger |
2296
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2297
|
|
|
if ($result < 0) { |
2298
|
|
|
$error++; |
2299
|
|
|
} |
2300
|
|
|
// End call triggers |
2301
|
|
|
} |
2302
|
|
|
|
2303
|
|
|
if (!$error) { |
2304
|
|
|
$this->db->commit(); |
2305
|
|
|
return 1; |
2306
|
|
|
} else { |
2307
|
|
|
foreach ($this->errors as $errmsg) { |
2308
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2309
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2310
|
|
|
} |
2311
|
|
|
$this->db->rollback(); |
2312
|
|
|
return -1 * $error; |
2313
|
|
|
} |
2314
|
|
|
} |
2315
|
|
|
|
2316
|
|
|
return -1; |
2317
|
|
|
} |
2318
|
|
|
|
2319
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2320
|
|
|
|
2321
|
|
|
/** |
2322
|
|
|
* Define end validity date |
2323
|
|
|
* |
2324
|
|
|
* @param User $user Object user that modify |
2325
|
|
|
* @param int $date_end_validity End of validity date |
2326
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2327
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2328
|
|
|
*/ |
2329
|
|
|
public function set_echeance($user, $date_end_validity, $notrigger = 0) |
2330
|
|
|
{ |
2331
|
|
|
// phpcs:enable |
2332
|
|
|
if ($user->hasRight('propal', 'creer')) { |
2333
|
|
|
$error = 0; |
2334
|
|
|
|
2335
|
|
|
$this->db->begin(); |
2336
|
|
|
|
2337
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET fin_validite = " . ($date_end_validity != '' ? "'" . $this->db->idate($date_end_validity) . "'" : 'null'); |
2338
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2339
|
|
|
|
2340
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
2341
|
|
|
|
2342
|
|
|
$resql = $this->db->query($sql); |
2343
|
|
|
if (!$resql) { |
2344
|
|
|
$this->errors[] = $this->db->error(); |
2345
|
|
|
$error++; |
2346
|
|
|
} |
2347
|
|
|
|
2348
|
|
|
|
2349
|
|
|
if (!$error) { |
2350
|
|
|
$this->oldcopy = clone $this; |
2351
|
|
|
$this->fin_validite = $date_end_validity; |
2352
|
|
|
} |
2353
|
|
|
|
2354
|
|
|
if (!$notrigger && empty($error)) { |
2355
|
|
|
// Call trigger |
2356
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2357
|
|
|
if ($result < 0) { |
2358
|
|
|
$error++; |
2359
|
|
|
} |
2360
|
|
|
// End call triggers |
2361
|
|
|
} |
2362
|
|
|
|
2363
|
|
|
if (!$error) { |
2364
|
|
|
$this->db->commit(); |
2365
|
|
|
return 1; |
2366
|
|
|
} else { |
2367
|
|
|
foreach ($this->errors as $errmsg) { |
2368
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2369
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2370
|
|
|
} |
2371
|
|
|
$this->db->rollback(); |
2372
|
|
|
return -1 * $error; |
2373
|
|
|
} |
2374
|
|
|
} |
2375
|
|
|
|
2376
|
|
|
return -1; |
2377
|
|
|
} |
2378
|
|
|
|
2379
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2380
|
|
|
|
2381
|
|
|
/** |
2382
|
|
|
* Set delivery date |
2383
|
|
|
* |
2384
|
|
|
* @param User $user Object user that modify |
2385
|
|
|
* @param int $delivery_date Delivery date |
2386
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2387
|
|
|
* @return int Return integer <0 if ko, >0 if ok |
2388
|
|
|
* @deprecated Use setDeliveryDate |
2389
|
|
|
*/ |
2390
|
|
|
public function set_date_livraison($user, $delivery_date, $notrigger = 0) |
2391
|
|
|
{ |
2392
|
|
|
// phpcs:enable |
2393
|
|
|
return $this->setDeliveryDate($user, $delivery_date, $notrigger); |
2394
|
|
|
} |
2395
|
|
|
|
2396
|
|
|
/** |
2397
|
|
|
* Set delivery date |
2398
|
|
|
* |
2399
|
|
|
* @param User $user Object user that modify |
2400
|
|
|
* @param int $delivery_date Delivery date |
2401
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2402
|
|
|
* @return int Return integer <0 if ko, >0 if ok |
2403
|
|
|
*/ |
2404
|
|
|
public function setDeliveryDate($user, $delivery_date, $notrigger = 0) |
2405
|
|
|
{ |
2406
|
|
|
if ($user->hasRight('propal', 'creer')) { |
2407
|
|
|
$error = 0; |
2408
|
|
|
|
2409
|
|
|
$this->db->begin(); |
2410
|
|
|
|
2411
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal "; |
2412
|
|
|
$sql .= " SET date_livraison = " . ($delivery_date != '' ? "'" . $this->db->idate($delivery_date) . "'" : 'null'); |
2413
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2414
|
|
|
|
2415
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
2416
|
|
|
$resql = $this->db->query($sql); |
2417
|
|
|
if (!$resql) { |
2418
|
|
|
$this->errors[] = $this->db->error(); |
2419
|
|
|
$error++; |
2420
|
|
|
} |
2421
|
|
|
|
2422
|
|
|
if (!$error) { |
2423
|
|
|
$this->oldcopy = clone $this; |
2424
|
|
|
$this->delivery_date = $delivery_date; |
2425
|
|
|
} |
2426
|
|
|
|
2427
|
|
|
if (!$notrigger && empty($error)) { |
2428
|
|
|
// Call trigger |
2429
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2430
|
|
|
if ($result < 0) { |
2431
|
|
|
$error++; |
2432
|
|
|
} |
2433
|
|
|
// End call triggers |
2434
|
|
|
} |
2435
|
|
|
|
2436
|
|
|
if (!$error) { |
2437
|
|
|
$this->db->commit(); |
2438
|
|
|
return 1; |
2439
|
|
|
} else { |
2440
|
|
|
foreach ($this->errors as $errmsg) { |
2441
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2442
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2443
|
|
|
} |
2444
|
|
|
$this->db->rollback(); |
2445
|
|
|
return -1 * $error; |
2446
|
|
|
} |
2447
|
|
|
} |
2448
|
|
|
|
2449
|
|
|
return -1; |
2450
|
|
|
} |
2451
|
|
|
|
2452
|
|
|
/** |
2453
|
|
|
* Set delivery |
2454
|
|
|
* |
2455
|
|
|
* @param User $user Object user that modify |
2456
|
|
|
* @param int $id Availability id |
2457
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2458
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2459
|
|
|
*/ |
2460
|
|
|
public function set_availability($user, $id, $notrigger = 0) |
2461
|
|
|
{ |
2462
|
|
|
// phpcs:enable |
2463
|
|
|
if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) { |
2464
|
|
|
$error = 0; |
2465
|
|
|
|
2466
|
|
|
$this->db->begin(); |
2467
|
|
|
|
2468
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
2469
|
|
|
$sql .= " SET fk_availability = " . ((int)$id); |
2470
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2471
|
|
|
|
2472
|
|
|
dol_syslog(__METHOD__ . ' availability(' . $id . ')', LOG_DEBUG); |
2473
|
|
|
$resql = $this->db->query($sql); |
2474
|
|
|
if (!$resql) { |
2475
|
|
|
$this->errors[] = $this->db->error(); |
2476
|
|
|
$error++; |
2477
|
|
|
} |
2478
|
|
|
|
2479
|
|
|
if (!$error) { |
2480
|
|
|
$this->oldcopy = clone $this; |
2481
|
|
|
$this->fk_availability = $id; |
2482
|
|
|
$this->availability_id = $id; |
2483
|
|
|
} |
2484
|
|
|
|
2485
|
|
|
if (!$notrigger && empty($error)) { |
2486
|
|
|
// Call trigger |
2487
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2488
|
|
|
if ($result < 0) { |
2489
|
|
|
$error++; |
2490
|
|
|
} |
2491
|
|
|
// End call triggers |
2492
|
|
|
} |
2493
|
|
|
|
2494
|
|
|
if (!$error) { |
2495
|
|
|
$this->db->commit(); |
2496
|
|
|
return 1; |
2497
|
|
|
} else { |
2498
|
|
|
foreach ($this->errors as $errmsg) { |
2499
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2500
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2501
|
|
|
} |
2502
|
|
|
$this->db->rollback(); |
2503
|
|
|
return -1 * $error; |
2504
|
|
|
} |
2505
|
|
|
} else { |
2506
|
|
|
$error_str = 'Propal status do not meet requirement ' . $this->statut; |
2507
|
|
|
dol_syslog(__METHOD__ . $error_str, LOG_ERR); |
2508
|
|
|
$this->error = $error_str; |
2509
|
|
|
$this->errors[] = $this->error; |
2510
|
|
|
return -2; |
2511
|
|
|
} |
2512
|
|
|
} |
2513
|
|
|
|
2514
|
|
|
/** |
2515
|
|
|
* Set source of demand |
2516
|
|
|
* |
2517
|
|
|
* @param User $user Object user that modify |
2518
|
|
|
* @param int $id Input reason id |
2519
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2520
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2521
|
|
|
*/ |
2522
|
|
|
public function set_demand_reason($user, $id, $notrigger = 0) |
2523
|
|
|
{ |
2524
|
|
|
// phpcs:enable |
2525
|
|
|
if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) { |
2526
|
|
|
$error = 0; |
2527
|
|
|
|
2528
|
|
|
$this->db->begin(); |
2529
|
|
|
|
2530
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal "; |
2531
|
|
|
$sql .= " SET fk_input_reason = " . ((int)$id); |
2532
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2533
|
|
|
|
2534
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
2535
|
|
|
$resql = $this->db->query($sql); |
2536
|
|
|
if (!$resql) { |
2537
|
|
|
$this->errors[] = $this->db->error(); |
2538
|
|
|
$error++; |
2539
|
|
|
} |
2540
|
|
|
|
2541
|
|
|
|
2542
|
|
|
if (!$error) { |
2543
|
|
|
$this->oldcopy = clone $this; |
2544
|
|
|
|
2545
|
|
|
$this->demand_reason_id = $id; |
2546
|
|
|
} |
2547
|
|
|
|
2548
|
|
|
|
2549
|
|
|
if (!$notrigger && empty($error)) { |
2550
|
|
|
// Call trigger |
2551
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2552
|
|
|
if ($result < 0) { |
2553
|
|
|
$error++; |
2554
|
|
|
} |
2555
|
|
|
// End call triggers |
2556
|
|
|
} |
2557
|
|
|
|
2558
|
|
|
if (!$error) { |
2559
|
|
|
$this->db->commit(); |
2560
|
|
|
return 1; |
2561
|
|
|
} else { |
2562
|
|
|
foreach ($this->errors as $errmsg) { |
2563
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2564
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2565
|
|
|
} |
2566
|
|
|
$this->db->rollback(); |
2567
|
|
|
return -1 * $error; |
2568
|
|
|
} |
2569
|
|
|
} else { |
2570
|
|
|
$error_str = 'Propal status do not meet requirement ' . $this->statut; |
2571
|
|
|
dol_syslog(__METHOD__ . $error_str, LOG_ERR); |
2572
|
|
|
$this->error = $error_str; |
2573
|
|
|
$this->errors[] = $this->error; |
2574
|
|
|
return -2; |
2575
|
|
|
} |
2576
|
|
|
} |
2577
|
|
|
|
2578
|
|
|
/** |
2579
|
|
|
* Set customer reference number |
2580
|
|
|
* |
2581
|
|
|
* @param User $user Object user that modify |
2582
|
|
|
* @param string $ref_client Customer reference |
2583
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2584
|
|
|
* @return int Return integer <0 if ko, >0 if ok |
2585
|
|
|
*/ |
2586
|
|
|
public function set_ref_client($user, $ref_client, $notrigger = 0) |
2587
|
|
|
{ |
2588
|
|
|
// phpcs:enable |
2589
|
|
|
if ($user->hasRight('propal', 'creer')) { |
2590
|
|
|
$error = 0; |
2591
|
|
|
|
2592
|
|
|
$this->db->begin(); |
2593
|
|
|
|
2594
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET ref_client = " . (empty($ref_client) ? 'NULL' : "'" . $this->db->escape($ref_client) . "'"); |
2595
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2596
|
|
|
|
2597
|
|
|
dol_syslog(__METHOD__ . ' $this->id=' . $this->id . ', ref_client=' . $ref_client, LOG_DEBUG); |
2598
|
|
|
$resql = $this->db->query($sql); |
2599
|
|
|
if (!$resql) { |
2600
|
|
|
$this->errors[] = $this->db->error(); |
2601
|
|
|
$error++; |
2602
|
|
|
} |
2603
|
|
|
|
2604
|
|
|
if (!$error) { |
2605
|
|
|
$this->oldcopy = clone $this; |
2606
|
|
|
$this->ref_client = $ref_client; |
2607
|
|
|
} |
2608
|
|
|
|
2609
|
|
|
if (!$notrigger && empty($error)) { |
2610
|
|
|
// Call trigger |
2611
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
2612
|
|
|
if ($result < 0) { |
2613
|
|
|
$error++; |
2614
|
|
|
} |
2615
|
|
|
// End call triggers |
2616
|
|
|
} |
2617
|
|
|
|
2618
|
|
|
if (!$error) { |
2619
|
|
|
$this->db->commit(); |
2620
|
|
|
return 1; |
2621
|
|
|
} else { |
2622
|
|
|
foreach ($this->errors as $errmsg) { |
2623
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2624
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2625
|
|
|
} |
2626
|
|
|
$this->db->rollback(); |
2627
|
|
|
return -1 * $error; |
2628
|
|
|
} |
2629
|
|
|
} |
2630
|
|
|
|
2631
|
|
|
return -1; |
2632
|
|
|
} |
2633
|
|
|
|
2634
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2635
|
|
|
|
2636
|
|
|
/** |
2637
|
|
|
* Reopen the commercial proposal |
2638
|
|
|
* |
2639
|
|
|
* @param User $user Object user that close |
2640
|
|
|
* @param int $status Status |
2641
|
|
|
* @param string $note Comment |
2642
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2643
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2644
|
|
|
*/ |
2645
|
|
|
public function reopen($user, $status, $note = '', $notrigger = 0) |
2646
|
|
|
{ |
2647
|
|
|
$error = 0; |
2648
|
|
|
|
2649
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
2650
|
|
|
$sql .= " SET fk_statut = " . ((int)$status) . ","; |
2651
|
|
|
if (!empty($note)) { |
2652
|
|
|
$sql .= " note_private = '" . $this->db->escape($note) . "',"; |
2653
|
|
|
} |
2654
|
|
|
$sql .= " date_cloture=NULL, fk_user_cloture=NULL"; |
2655
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2656
|
|
|
|
2657
|
|
|
$this->db->begin(); |
2658
|
|
|
|
2659
|
|
|
dol_syslog(get_only_class($this) . "::reopen", LOG_DEBUG); |
2660
|
|
|
$resql = $this->db->query($sql); |
2661
|
|
|
if (!$resql) { |
2662
|
|
|
$error++; |
2663
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
2664
|
|
|
} |
2665
|
|
|
if (!$error) { |
2666
|
|
|
if (!$notrigger) { |
2667
|
|
|
// Call trigger |
2668
|
|
|
$result = $this->call_trigger('PROPAL_REOPEN', $user); |
2669
|
|
|
if ($result < 0) { |
2670
|
|
|
$error++; |
2671
|
|
|
} |
2672
|
|
|
// End call triggers |
2673
|
|
|
} |
2674
|
|
|
} |
2675
|
|
|
|
2676
|
|
|
// Commit or rollback |
2677
|
|
|
if ($error) { |
2678
|
|
|
if (!empty($this->errors)) { |
2679
|
|
|
foreach ($this->errors as $errmsg) { |
2680
|
|
|
dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR); |
2681
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2682
|
|
|
} |
2683
|
|
|
} |
2684
|
|
|
$this->db->rollback(); |
2685
|
|
|
return -1 * $error; |
2686
|
|
|
} else { |
2687
|
|
|
$this->statut = $status; |
2688
|
|
|
$this->status = $status; |
2689
|
|
|
|
2690
|
|
|
$this->db->commit(); |
2691
|
|
|
return 1; |
2692
|
|
|
} |
2693
|
|
|
} |
2694
|
|
|
|
2695
|
|
|
|
2696
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2697
|
|
|
|
2698
|
|
|
/** |
2699
|
|
|
* Close/set the commercial proposal to status signed or refused (fill also date signature) |
2700
|
|
|
* |
2701
|
|
|
* @param User $user Object user that close |
2702
|
|
|
* @param int $status Status (self::STATUS_BILLED or self::STATUS_REFUSED) |
2703
|
|
|
* @param string $note Complete private note with this note |
2704
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0=Execute triggers |
2705
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2706
|
|
|
*/ |
2707
|
|
|
public function closeProposal($user, $status, $note = '', $notrigger = 0) |
2708
|
|
|
{ |
2709
|
|
|
global $langs, $conf; |
2710
|
|
|
|
2711
|
|
|
$error = 0; |
2712
|
|
|
$now = dol_now(); |
2713
|
|
|
|
2714
|
|
|
$this->db->begin(); |
2715
|
|
|
|
2716
|
|
|
$newprivatenote = dol_concatdesc($this->note_private, $note); |
2717
|
|
|
|
2718
|
|
|
if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) { |
2719
|
|
|
$date_signature = $now; |
2720
|
|
|
$fk_user_signature = $user->id; |
2721
|
|
|
} else { |
2722
|
|
|
$this->info($this->id); |
2723
|
|
|
if (!isset($this->date_signature) || $this->date_signature == '') { |
2724
|
|
|
$date_signature = $now; |
2725
|
|
|
$fk_user_signature = $user->id; |
2726
|
|
|
} else { |
2727
|
|
|
$date_signature = $this->date_signature; |
2728
|
|
|
$fk_user_signature = $this->user_signature->id; |
2729
|
|
|
} |
2730
|
|
|
} |
2731
|
|
|
|
2732
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
2733
|
|
|
$sql .= " SET fk_statut = " . ((int)$status) . ", note_private = '" . $this->db->escape($newprivatenote) . "', date_signature='" . $this->db->idate($date_signature) . "', fk_user_signature=" . $fk_user_signature; |
2734
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2735
|
|
|
|
2736
|
|
|
$resql = $this->db->query($sql); |
2737
|
|
|
if ($resql) { |
2738
|
|
|
// Status self::STATUS_REFUSED by default |
2739
|
|
|
$modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf); |
2740
|
|
|
$trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger() |
2741
|
|
|
|
2742
|
|
|
if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED |
2743
|
|
|
$trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger() |
2744
|
|
|
$modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf; |
2745
|
|
|
|
2746
|
|
|
// The connected company is classified as a client |
2747
|
|
|
$soc = new Societe($this->db); |
2748
|
|
|
$soc->id = $this->socid; |
2749
|
|
|
$result = $soc->setAsCustomer(); |
2750
|
|
|
|
2751
|
|
|
if ($result < 0) { |
2752
|
|
|
$this->error = $this->db->lasterror(); |
2753
|
|
|
$this->db->rollback(); |
2754
|
|
|
return -2; |
2755
|
|
|
} |
2756
|
|
|
} |
2757
|
|
|
|
2758
|
|
|
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) { |
2759
|
|
|
// Define output language |
2760
|
|
|
$outputlangs = $langs; |
2761
|
|
|
if (getDolGlobalInt('MAIN_MULTILANGS')) { |
2762
|
|
|
$outputlangs = new Translate("", $conf); |
2763
|
|
|
$newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang); |
2764
|
|
|
$outputlangs->setDefaultLang($newlang); |
2765
|
|
|
} |
2766
|
|
|
|
2767
|
|
|
// PDF |
2768
|
|
|
$hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0); |
2769
|
|
|
$hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0); |
2770
|
|
|
$hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0); |
2771
|
|
|
|
2772
|
|
|
//$ret=$object->fetch($id); // Reload to get new records |
2773
|
|
|
$this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); |
2774
|
|
|
} |
2775
|
|
|
|
2776
|
|
|
if (!$error) { |
2777
|
|
|
$this->oldcopy = clone $this; |
2778
|
|
|
$this->statut = $status; |
2779
|
|
|
$this->status = $status; |
2780
|
|
|
$this->date_signature = $date_signature; |
2781
|
|
|
$this->note_private = $newprivatenote; |
2782
|
|
|
} |
2783
|
|
|
|
2784
|
|
|
if (!$notrigger && empty($error)) { |
2785
|
|
|
// Call trigger |
2786
|
|
|
$result = $this->call_trigger($trigger_name, $user); |
2787
|
|
|
if ($result < 0) { |
2788
|
|
|
$error++; |
2789
|
|
|
} |
2790
|
|
|
// End call triggers |
2791
|
|
|
} |
2792
|
|
|
|
2793
|
|
|
if (!$error) { |
2794
|
|
|
$this->db->commit(); |
2795
|
|
|
return 1; |
2796
|
|
|
} else { |
2797
|
|
|
$this->statut = $this->oldcopy->statut; |
2798
|
|
|
$this->status = $this->oldcopy->statut; |
2799
|
|
|
$this->date_signature = $this->oldcopy->date_signature; |
2800
|
|
|
$this->note_private = $this->oldcopy->note_private; |
2801
|
|
|
|
2802
|
|
|
$this->db->rollback(); |
2803
|
|
|
return -1; |
2804
|
|
|
} |
2805
|
|
|
} else { |
2806
|
|
|
$this->error = $this->db->lasterror(); |
2807
|
|
|
$this->db->rollback(); |
2808
|
|
|
return -1; |
2809
|
|
|
} |
2810
|
|
|
} |
2811
|
|
|
|
2812
|
|
|
/** |
2813
|
|
|
* Object Proposal Information |
2814
|
|
|
* |
2815
|
|
|
* @param int $id Proposal id |
2816
|
|
|
* @return void |
2817
|
|
|
*/ |
2818
|
|
|
public function info($id) |
2819
|
|
|
{ |
2820
|
|
|
$sql = "SELECT c.rowid, "; |
2821
|
|
|
$sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,"; |
2822
|
|
|
$sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture"; |
2823
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "propal as c"; |
2824
|
|
|
$sql .= " WHERE c.rowid = " . ((int)$id); |
2825
|
|
|
|
2826
|
|
|
$result = $this->db->query($sql); |
2827
|
|
|
|
2828
|
|
|
if ($result) { |
2829
|
|
|
if ($this->db->num_rows($result)) { |
2830
|
|
|
$obj = $this->db->fetch_object($result); |
2831
|
|
|
|
2832
|
|
|
$this->id = $obj->rowid; |
2833
|
|
|
|
2834
|
|
|
$this->date_creation = $this->db->jdate($obj->datec); |
2835
|
|
|
$this->date_validation = $this->db->jdate($obj->datev); |
2836
|
|
|
$this->date_signature = $this->db->jdate($obj->date_signature); |
2837
|
|
|
$this->date_cloture = $this->db->jdate($obj->date_cloture); |
2838
|
|
|
|
2839
|
|
|
$this->user_creation_id = $obj->fk_user_author; |
2840
|
|
|
$this->user_validation_id = $obj->fk_user_valid; |
2841
|
|
|
|
2842
|
|
|
if ($obj->fk_user_signature) { |
2843
|
|
|
$user_signature = new User($this->db); |
2844
|
|
|
$user_signature->fetch($obj->fk_user_signature); |
2845
|
|
|
$this->user_signature = $user_signature; |
2846
|
|
|
} |
2847
|
|
|
|
2848
|
|
|
$this->user_closing_id = $obj->fk_user_cloture; |
2849
|
|
|
} |
2850
|
|
|
$this->db->free($result); |
2851
|
|
|
} else { |
2852
|
|
|
dol_print_error($this->db); |
2853
|
|
|
} |
2854
|
|
|
} |
2855
|
|
|
|
2856
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2857
|
|
|
|
2858
|
|
|
/** |
2859
|
|
|
* Create a document onto disk according to template module. |
2860
|
|
|
* |
2861
|
|
|
* @param string $modele Force model to use ('' to not force) |
2862
|
|
|
* @param Translate $outputlangs Object langs to use for output |
2863
|
|
|
* @param int $hidedetails Hide details of lines |
2864
|
|
|
* @param int $hidedesc Hide description |
2865
|
|
|
* @param int $hideref Hide ref |
2866
|
|
|
* @param null|array $moreparams Array to provide more information |
2867
|
|
|
* @return int 0 if KO, 1 if OK |
2868
|
|
|
*/ |
2869
|
|
|
public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) |
2870
|
|
|
{ |
2871
|
|
|
global $conf, $langs; |
2872
|
|
|
|
2873
|
|
|
$langs->load("propale"); |
2874
|
|
|
$outputlangs->load("products"); |
2875
|
|
|
|
2876
|
|
|
if (!dol_strlen($modele)) { |
2877
|
|
|
$modele = 'azur'; |
2878
|
|
|
|
2879
|
|
|
if ($this->model_pdf) { |
2880
|
|
|
$modele = $this->model_pdf; |
2881
|
|
|
} elseif (getDolGlobalString('PROPALE_ADDON_PDF')) { |
2882
|
|
|
$modele = getDolGlobalString('PROPALE_ADDON_PDF'); |
2883
|
|
|
} |
2884
|
|
|
} |
2885
|
|
|
|
2886
|
|
|
$modelpath = "core/modules/propale/doc/"; |
2887
|
|
|
|
2888
|
|
|
return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); |
2889
|
|
|
} |
2890
|
|
|
|
2891
|
|
|
/** |
2892
|
|
|
* Classify the proposal to status Billed |
2893
|
|
|
* |
2894
|
|
|
* @param User $user Object user |
2895
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2896
|
|
|
* @param string $note Complete private note with this note |
2897
|
|
|
* @return int Return integer <0 if KO, 0 = nothing done, >0 if OK |
2898
|
|
|
*/ |
2899
|
|
|
public function classifyBilled(User $user, $notrigger = 0, $note = '') |
2900
|
|
|
{ |
2901
|
|
|
global $conf, $langs; |
2902
|
|
|
|
2903
|
|
|
$error = 0; |
2904
|
|
|
|
2905
|
|
|
$now = dol_now(); |
2906
|
|
|
$num = 0; |
2907
|
|
|
|
2908
|
|
|
$triggerName = 'PROPAL_CLASSIFY_BILLED'; |
2909
|
|
|
|
2910
|
|
|
$this->db->begin(); |
2911
|
|
|
|
2912
|
|
|
$newprivatenote = dol_concatdesc($this->note_private, $note); |
2913
|
|
|
|
2914
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal SET fk_statut = ' . self::STATUS_BILLED . ", "; |
2915
|
|
|
$sql .= " note_private = '" . $this->db->escape($newprivatenote) . "', date_cloture='" . $this->db->idate($now) . "', fk_user_cloture=" . ((int)$user->id); |
2916
|
|
|
$sql .= ' WHERE rowid = ' . ((int)$this->id) . ' AND fk_statut = ' . ((int)self::STATUS_SIGNED); |
2917
|
|
|
|
2918
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
2919
|
|
|
$resql = $this->db->query($sql); |
2920
|
|
|
if (!$resql) { |
2921
|
|
|
$this->errors[] = $this->db->error(); |
2922
|
|
|
$error++; |
2923
|
|
|
} else { |
2924
|
|
|
$num = $this->db->affected_rows($resql); |
2925
|
|
|
} |
2926
|
|
|
|
2927
|
|
|
if (!$error) { |
2928
|
|
|
$modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf); |
2929
|
|
|
|
2930
|
|
|
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { |
2931
|
|
|
// Define output language |
2932
|
|
|
$outputlangs = $langs; |
2933
|
|
|
if (getDolGlobalInt('MAIN_MULTILANGS')) { |
2934
|
|
|
$outputlangs = new Translate("", $conf); |
2935
|
|
|
$newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang); |
2936
|
|
|
$outputlangs->setDefaultLang($newlang); |
2937
|
|
|
} |
2938
|
|
|
|
2939
|
|
|
// PDF |
2940
|
|
|
$hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0); |
2941
|
|
|
$hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0); |
2942
|
|
|
$hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0); |
2943
|
|
|
|
2944
|
|
|
//$ret=$object->fetch($id); // Reload to get new records |
2945
|
|
|
$this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); |
2946
|
|
|
} |
2947
|
|
|
|
2948
|
|
|
$this->oldcopy = clone $this; |
2949
|
|
|
$this->statut = self::STATUS_BILLED; |
2950
|
|
|
$this->date_cloture = $now; |
2951
|
|
|
$this->note_private = $newprivatenote; |
2952
|
|
|
} |
2953
|
|
|
|
2954
|
|
|
if (!$notrigger && empty($error)) { |
2955
|
|
|
// Call trigger |
2956
|
|
|
$result = $this->call_trigger($triggerName, $user); |
2957
|
|
|
if ($result < 0) { |
2958
|
|
|
$error++; |
2959
|
|
|
} |
2960
|
|
|
// End call triggers |
2961
|
|
|
} |
2962
|
|
|
|
2963
|
|
|
if (!$error) { |
2964
|
|
|
$this->db->commit(); |
2965
|
|
|
return $num; |
2966
|
|
|
} else { |
2967
|
|
|
foreach ($this->errors as $errmsg) { |
2968
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
2969
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2970
|
|
|
} |
2971
|
|
|
$this->db->rollback(); |
2972
|
|
|
return -1 * $error; |
2973
|
|
|
} |
2974
|
|
|
} |
2975
|
|
|
|
2976
|
|
|
/** |
2977
|
|
|
* Cancel the proposal |
2978
|
|
|
* |
2979
|
|
|
* @param User $user Object user |
2980
|
|
|
* @return int Return integer if KO <0 , if OK >0 |
2981
|
|
|
*/ |
2982
|
|
|
public function setCancel(User $user) |
2983
|
|
|
{ |
2984
|
|
|
$error = 0; |
2985
|
|
|
|
2986
|
|
|
$this->db->begin(); |
2987
|
|
|
|
2988
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
2989
|
|
|
$sql .= " SET fk_statut = " . self::STATUS_CANCELED . ","; |
2990
|
|
|
$sql .= " fk_user_modif = " . ((int)$user->id); |
2991
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
2992
|
|
|
|
2993
|
|
|
dol_syslog(get_only_class($this) . "::cancel", LOG_DEBUG); |
2994
|
|
|
if ($this->db->query($sql)) { |
2995
|
|
|
if (!$error) { |
2996
|
|
|
// Call trigger |
2997
|
|
|
$result = $this->call_trigger('PROPAL_CANCEL', $user); |
2998
|
|
|
if ($result < 0) { |
2999
|
|
|
$error++; |
3000
|
|
|
} |
3001
|
|
|
// End call triggers |
3002
|
|
|
} |
3003
|
|
|
|
3004
|
|
|
if (!$error) { |
3005
|
|
|
$this->statut = self::STATUS_CANCELED; |
3006
|
|
|
$this->db->commit(); |
3007
|
|
|
return 1; |
3008
|
|
|
} else { |
3009
|
|
|
foreach ($this->errors as $errmsg) { |
3010
|
|
|
dol_syslog(get_only_class($this) . "::cancel " . $errmsg, LOG_ERR); |
3011
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
3012
|
|
|
} |
3013
|
|
|
$this->db->rollback(); |
3014
|
|
|
return -1; |
3015
|
|
|
} |
3016
|
|
|
} else { |
3017
|
|
|
$this->error = $this->db->error(); |
3018
|
|
|
$this->db->rollback(); |
3019
|
|
|
return -1; |
3020
|
|
|
} |
3021
|
|
|
} |
3022
|
|
|
|
3023
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
3024
|
|
|
|
3025
|
|
|
/** |
3026
|
|
|
* Set draft status |
3027
|
|
|
* |
3028
|
|
|
* @param User $user Object user that modify |
3029
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
3030
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
3031
|
|
|
*/ |
3032
|
|
|
public function setDraft($user, $notrigger = 0) |
3033
|
|
|
{ |
3034
|
|
|
// phpcs:enable |
3035
|
|
|
$error = 0; |
3036
|
|
|
|
3037
|
|
|
// Protection |
3038
|
|
|
if ($this->statut <= self::STATUS_DRAFT) { |
3039
|
|
|
return 0; |
3040
|
|
|
} |
3041
|
|
|
|
3042
|
|
|
dol_syslog(get_only_class($this) . "::setDraft", LOG_DEBUG); |
3043
|
|
|
|
3044
|
|
|
$this->db->begin(); |
3045
|
|
|
|
3046
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "propal"; |
3047
|
|
|
$sql .= " SET fk_statut = " . self::STATUS_DRAFT; |
3048
|
|
|
$sql .= ", online_sign_ip = NULL , online_sign_name = NULL"; |
3049
|
|
|
$sql .= " WHERE rowid = " . ((int)$this->id); |
3050
|
|
|
|
3051
|
|
|
$resql = $this->db->query($sql); |
3052
|
|
|
if (!$resql) { |
3053
|
|
|
$this->errors[] = $this->db->error(); |
3054
|
|
|
$error++; |
3055
|
|
|
} |
3056
|
|
|
|
3057
|
|
|
if (!$error) { |
3058
|
|
|
$this->oldcopy = clone $this; |
3059
|
|
|
} |
3060
|
|
|
|
3061
|
|
|
if (!$notrigger && empty($error)) { |
3062
|
|
|
// Call trigger |
3063
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
3064
|
|
|
if ($result < 0) { |
3065
|
|
|
$error++; |
3066
|
|
|
} |
3067
|
|
|
// End call triggers |
3068
|
|
|
} |
3069
|
|
|
|
3070
|
|
|
if (!$error) { |
3071
|
|
|
$this->statut = self::STATUS_DRAFT; |
3072
|
|
|
$this->status = self::STATUS_DRAFT; |
3073
|
|
|
|
3074
|
|
|
$this->db->commit(); |
3075
|
|
|
return 1; |
3076
|
|
|
} else { |
3077
|
|
|
foreach ($this->errors as $errmsg) { |
3078
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
3079
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
3080
|
|
|
} |
3081
|
|
|
$this->db->rollback(); |
3082
|
|
|
return -1 * $error; |
3083
|
|
|
} |
3084
|
|
|
} |
3085
|
|
|
|
3086
|
|
|
/** |
3087
|
|
|
* Return list of proposal (eventually filtered on user) into an array |
3088
|
|
|
* |
3089
|
|
|
* @param int $shortlist 0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name) |
3090
|
|
|
* @param int $draft 0=not draft, 1=draft |
3091
|
|
|
* @param int $notcurrentuser 0=all user, 1=not current user |
3092
|
|
|
* @param int $socid Id third party |
3093
|
|
|
* @param int $limit For pagination |
3094
|
|
|
* @param int $offset For pagination |
3095
|
|
|
* @param string $sortfield Sort criteria |
3096
|
|
|
* @param string $sortorder Sort order |
3097
|
|
|
* @return array|int -1 if KO, array with result if OK |
3098
|
|
|
*/ |
3099
|
|
|
public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC') |
3100
|
|
|
{ |
3101
|
|
|
// phpcs:enable |
3102
|
|
|
global $user; |
3103
|
|
|
|
3104
|
|
|
$ga = array(); |
3105
|
|
|
|
3106
|
|
|
$sql = "SELECT s.rowid, s.nom as name, s.client,"; |
3107
|
|
|
$sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, "; |
3108
|
|
|
$sql .= " p.datep as dp, p.fin_validite as datelimite"; |
3109
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "societe as s, " . MAIN_DB_PREFIX . "propal as p, " . MAIN_DB_PREFIX . "c_propalst as c"; |
3110
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('propal') . ")"; |
3111
|
|
|
$sql .= " AND p.fk_soc = s.rowid"; |
3112
|
|
|
$sql .= " AND p.fk_statut = c.id"; |
3113
|
|
|
|
3114
|
|
|
// If the internal user must only see his customers, force searching by him |
3115
|
|
|
$search_sale = 0; |
3116
|
|
|
if (!$user->hasRight('societe', 'client', 'voir')) { |
3117
|
|
|
$search_sale = $user->id; |
3118
|
|
|
} |
3119
|
|
|
// Search on sale representative |
3120
|
|
|
if ($search_sale && $search_sale != '-1') { |
3121
|
|
|
if ($search_sale == -2) { |
3122
|
|
|
$sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)"; |
3123
|
|
|
} elseif ($search_sale > 0) { |
3124
|
|
|
$sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int)$search_sale) . ")"; |
3125
|
|
|
} |
3126
|
|
|
} |
3127
|
|
|
// Search on socid |
3128
|
|
|
if ($socid) { |
3129
|
|
|
$sql .= " AND p.fk_soc = " . ((int)$socid); |
3130
|
|
|
} |
3131
|
|
|
if ($draft) { |
3132
|
|
|
$sql .= " AND p.fk_statut = " . ((int)self::STATUS_DRAFT); |
3133
|
|
|
} |
3134
|
|
|
if ($notcurrentuser > 0) { |
3135
|
|
|
$sql .= " AND p.fk_user_author <> " . ((int)$user->id); |
3136
|
|
|
} |
3137
|
|
|
$sql .= $this->db->order($sortfield, $sortorder); |
3138
|
|
|
$sql .= $this->db->plimit($limit, $offset); |
3139
|
|
|
|
3140
|
|
|
$result = $this->db->query($sql); |
3141
|
|
|
if ($result) { |
3142
|
|
|
$num = $this->db->num_rows($result); |
3143
|
|
|
if ($num) { |
3144
|
|
|
$i = 0; |
3145
|
|
|
while ($i < $num) { |
3146
|
|
|
$obj = $this->db->fetch_object($result); |
3147
|
|
|
|
3148
|
|
|
if ($shortlist == 1) { |
3149
|
|
|
$ga[$obj->propalid] = $obj->ref; |
3150
|
|
|
} elseif ($shortlist == 2) { |
3151
|
|
|
$ga[$obj->propalid] = $obj->ref . ' (' . $obj->name . ')'; |
3152
|
|
|
} else { |
3153
|
|
|
$ga[$i]['id'] = $obj->propalid; |
3154
|
|
|
$ga[$i]['ref'] = $obj->ref; |
3155
|
|
|
$ga[$i]['name'] = $obj->name; |
3156
|
|
|
} |
3157
|
|
|
|
3158
|
|
|
$i++; |
3159
|
|
|
} |
3160
|
|
|
} |
3161
|
|
|
return $ga; |
3162
|
|
|
} else { |
3163
|
|
|
dol_print_error($this->db); |
3164
|
|
|
return -1; |
3165
|
|
|
} |
3166
|
|
|
} |
3167
|
|
|
|
3168
|
|
|
/** |
3169
|
|
|
* Returns an array with the numbers of related invoices |
3170
|
|
|
* |
3171
|
|
|
* @return array Array of invoices |
3172
|
|
|
*/ |
3173
|
|
|
public function getInvoiceArrayList() |
3174
|
|
|
{ |
3175
|
|
|
return $this->InvoiceArrayList($this->id); |
3176
|
|
|
} |
3177
|
|
|
|
3178
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
3179
|
|
|
|
3180
|
|
|
/** |
3181
|
|
|
* Returns an array with id and ref of related invoices |
3182
|
|
|
* |
3183
|
|
|
* @param int $id Id propal |
3184
|
|
|
* @return array|int Array of invoices id |
3185
|
|
|
*/ |
3186
|
|
|
public function InvoiceArrayList($id) |
3187
|
|
|
{ |
3188
|
|
|
// phpcs:enable |
3189
|
|
|
$ga = array(); |
3190
|
|
|
$linkedInvoices = array(); |
3191
|
|
|
|
3192
|
|
|
$this->fetchObjectLinked($id, $this->element); |
3193
|
|
|
foreach ($this->linkedObjectsIds as $objecttype => $objectid) { |
3194
|
|
|
// Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n |
3195
|
|
|
// On parcourt donc une liste d'objets en tant qu'objet unique |
3196
|
|
|
foreach ($objectid as $key => $object) { |
3197
|
|
|
// Cas des factures liees directement |
3198
|
|
|
if ($objecttype == 'facture') { |
3199
|
|
|
$linkedInvoices[] = $object; |
3200
|
|
|
} else { |
3201
|
|
|
// Cas des factures liees par un autre object (ex: commande) |
3202
|
|
|
$this->fetchObjectLinked($object, $objecttype); |
3203
|
|
|
foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) { |
3204
|
|
|
foreach ($subobjectid as $subkey => $subobject) { |
3205
|
|
|
if ($subobjecttype == 'facture') { |
3206
|
|
|
$linkedInvoices[] = $subobject; |
3207
|
|
|
} |
3208
|
|
|
} |
3209
|
|
|
} |
3210
|
|
|
} |
3211
|
|
|
} |
3212
|
|
|
} |
3213
|
|
|
|
3214
|
|
|
if (count($linkedInvoices) > 0) { |
3215
|
|
|
$sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye"; |
3216
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "facture"; |
3217
|
|
|
$sql .= " WHERE rowid IN (" . $this->db->sanitize(implode(',', $linkedInvoices)) . ")"; |
3218
|
|
|
|
3219
|
|
|
dol_syslog(get_only_class($this) . "::InvoiceArrayList", LOG_DEBUG); |
3220
|
|
|
$resql = $this->db->query($sql); |
3221
|
|
|
|
3222
|
|
|
if ($resql) { |
3223
|
|
|
$tab_sqlobj = array(); |
3224
|
|
|
$nump = $this->db->num_rows($resql); |
3225
|
|
|
for ($i = 0; $i < $nump; $i++) { |
3226
|
|
|
$sqlobj = $this->db->fetch_object($resql); |
3227
|
|
|
$tab_sqlobj[] = $sqlobj; |
3228
|
|
|
} |
3229
|
|
|
$this->db->free($resql); |
3230
|
|
|
|
3231
|
|
|
$nump = count($tab_sqlobj); |
3232
|
|
|
|
3233
|
|
|
if ($nump) { |
3234
|
|
|
$i = 0; |
3235
|
|
|
while ($i < $nump) { |
3236
|
|
|
$obj = array_shift($tab_sqlobj); |
3237
|
|
|
|
3238
|
|
|
$ga[$i] = $obj; |
3239
|
|
|
|
3240
|
|
|
$i++; |
3241
|
|
|
} |
3242
|
|
|
} |
3243
|
|
|
return $ga; |
3244
|
|
|
} else { |
3245
|
|
|
return -1; |
3246
|
|
|
} |
3247
|
|
|
} else { |
3248
|
|
|
return $ga; |
3249
|
|
|
} |
3250
|
|
|
} |
3251
|
|
|
|
3252
|
|
|
|
3253
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
3254
|
|
|
|
3255
|
|
|
/** |
3256
|
|
|
* Change the delivery time |
3257
|
|
|
* |
3258
|
|
|
* @param int $availability_id Id of new delivery time |
3259
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
3260
|
|
|
* @return int >0 if OK, <0 if KO |
3261
|
|
|
* @deprecated use set_availability |
3262
|
|
|
*/ |
3263
|
|
|
public function availability($availability_id, $notrigger = 0) |
3264
|
|
|
{ |
3265
|
|
|
global $user; |
3266
|
|
|
|
3267
|
|
|
if ($this->statut >= self::STATUS_DRAFT) { |
3268
|
|
|
$error = 0; |
3269
|
|
|
|
3270
|
|
|
$this->db->begin(); |
3271
|
|
|
|
3272
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal'; |
3273
|
|
|
$sql .= ' SET fk_availability = ' . ((int)$availability_id); |
3274
|
|
|
$sql .= ' WHERE rowid=' . ((int)$this->id); |
3275
|
|
|
|
3276
|
|
|
dol_syslog(__METHOD__ . ' availability(' . $availability_id . ')', LOG_DEBUG); |
3277
|
|
|
$resql = $this->db->query($sql); |
3278
|
|
|
if (!$resql) { |
3279
|
|
|
$this->errors[] = $this->db->error(); |
3280
|
|
|
$error++; |
3281
|
|
|
} |
3282
|
|
|
|
3283
|
|
|
if (!$error) { |
3284
|
|
|
$this->oldcopy = clone $this; |
3285
|
|
|
$this->availability_id = $availability_id; |
3286
|
|
|
} |
3287
|
|
|
|
3288
|
|
|
if (!$notrigger && empty($error)) { |
3289
|
|
|
// Call trigger |
3290
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
3291
|
|
|
if ($result < 0) { |
3292
|
|
|
$error++; |
3293
|
|
|
} |
3294
|
|
|
// End call triggers |
3295
|
|
|
} |
3296
|
|
|
|
3297
|
|
|
if (!$error) { |
3298
|
|
|
$this->db->commit(); |
3299
|
|
|
return 1; |
3300
|
|
|
} else { |
3301
|
|
|
foreach ($this->errors as $errmsg) { |
3302
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
3303
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
3304
|
|
|
} |
3305
|
|
|
$this->db->rollback(); |
3306
|
|
|
return -1 * $error; |
3307
|
|
|
} |
3308
|
|
|
} else { |
3309
|
|
|
$error_str = 'Propal status do not meet requirement ' . $this->statut; |
3310
|
|
|
dol_syslog(__METHOD__ . $error_str, LOG_ERR); |
3311
|
|
|
$this->error = $error_str; |
3312
|
|
|
$this->errors[] = $this->error; |
3313
|
|
|
return -2; |
3314
|
|
|
} |
3315
|
|
|
} |
3316
|
|
|
|
3317
|
|
|
/** |
3318
|
|
|
* Change source demand |
3319
|
|
|
* |
3320
|
|
|
* @param int $demand_reason_id Id of new source demand |
3321
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
3322
|
|
|
* @return int >0 si ok, <0 si ko |
3323
|
|
|
* @deprecated use set_demand_reason |
3324
|
|
|
*/ |
3325
|
|
|
public function demand_reason($demand_reason_id, $notrigger = 0) |
3326
|
|
|
{ |
3327
|
|
|
// phpcs:enable |
3328
|
|
|
global $user; |
3329
|
|
|
|
3330
|
|
|
if ($this->status >= self::STATUS_DRAFT) { |
3331
|
|
|
$error = 0; |
3332
|
|
|
|
3333
|
|
|
$this->db->begin(); |
3334
|
|
|
|
3335
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal'; |
3336
|
|
|
$sql .= ' SET fk_input_reason = ' . ((int)$demand_reason_id); |
3337
|
|
|
$sql .= ' WHERE rowid=' . ((int)$this->id); |
3338
|
|
|
|
3339
|
|
|
dol_syslog(__METHOD__ . ' demand_reason(' . $demand_reason_id . ')', LOG_DEBUG); |
3340
|
|
|
$resql = $this->db->query($sql); |
3341
|
|
|
if (!$resql) { |
3342
|
|
|
$this->errors[] = $this->db->error(); |
3343
|
|
|
$error++; |
3344
|
|
|
} |
3345
|
|
|
|
3346
|
|
|
if (!$error) { |
3347
|
|
|
$this->oldcopy = clone $this; |
3348
|
|
|
$this->demand_reason_id = $demand_reason_id; |
3349
|
|
|
} |
3350
|
|
|
|
3351
|
|
|
if (!$notrigger && empty($error)) { |
3352
|
|
|
// Call trigger |
3353
|
|
|
$result = $this->call_trigger('PROPAL_MODIFY', $user); |
3354
|
|
|
if ($result < 0) { |
3355
|
|
|
$error++; |
3356
|
|
|
} |
3357
|
|
|
// End call triggers |
3358
|
|
|
} |
3359
|
|
|
|
3360
|
|
|
if (!$error) { |
3361
|
|
|
$this->db->commit(); |
3362
|
|
|
return 1; |
3363
|
|
|
} else { |
3364
|
|
|
foreach ($this->errors as $errmsg) { |
3365
|
|
|
dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR); |
3366
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
3367
|
|
|
} |
3368
|
|
|
$this->db->rollback(); |
3369
|
|
|
return -1 * $error; |
3370
|
|
|
} |
3371
|
|
|
} else { |
3372
|
|
|
$error_str = 'Propal status do not meet requirement ' . $this->statut; |
3373
|
|
|
dol_syslog(__METHOD__ . $error_str, LOG_ERR); |
3374
|
|
|
$this->error = $error_str; |
3375
|
|
|
$this->errors[] = $this->error; |
3376
|
|
|
return -2; |
3377
|
|
|
} |
3378
|
|
|
} |
3379
|
|
|
|
3380
|
|
|
/** |
3381
|
|
|
* Load indicators for dashboard (this->nbtodo and this->nbtodolate) |
3382
|
|
|
* |
3383
|
|
|
* @param User $user Object user |
3384
|
|
|
* @param string $mode "opened" for proposal to close, "signed" for proposal to invoice |
3385
|
|
|
* @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK |
3386
|
|
|
*/ |
3387
|
|
|
public function load_board($user, $mode) |
3388
|
|
|
{ |
3389
|
|
|
// phpcs:enable |
3390
|
|
|
global $conf, $langs; |
3391
|
|
|
|
3392
|
|
|
$clause = " WHERE"; |
3393
|
|
|
|
3394
|
|
|
$sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht"; |
3395
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "propal as p"; |
3396
|
|
|
$sql .= $clause . " p.entity IN (" . getEntity('propal') . ")"; |
3397
|
|
|
if ($mode == 'opened') { |
3398
|
|
|
$sql .= " AND p.fk_statut = " . self::STATUS_VALIDATED; |
3399
|
|
|
} |
3400
|
|
|
if ($mode == 'signed') { |
3401
|
|
|
$sql .= " AND p.fk_statut = " . self::STATUS_SIGNED; |
3402
|
|
|
} |
3403
|
|
|
// If the internal user must only see his customers, force searching by him |
3404
|
|
|
$search_sale = 0; |
3405
|
|
|
if (!$user->hasRight('societe', 'client', 'voir')) { |
3406
|
|
|
$search_sale = $user->id; |
3407
|
|
|
} |
3408
|
|
|
// Search on sale representative |
3409
|
|
|
if ($search_sale && $search_sale != '-1') { |
3410
|
|
|
if ($search_sale == -2) { |
3411
|
|
|
$sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)"; |
3412
|
|
|
} elseif ($search_sale > 0) { |
3413
|
|
|
$sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int)$search_sale) . ")"; |
3414
|
|
|
} |
3415
|
|
|
} |
3416
|
|
|
|
3417
|
|
|
$resql = $this->db->query($sql); |
3418
|
|
|
if ($resql) { |
3419
|
|
|
$langs->load("propal"); |
3420
|
|
|
$now = dol_now(); |
3421
|
|
|
|
3422
|
|
|
$delay_warning = 0; |
3423
|
|
|
$status = 0; |
3424
|
|
|
$label = $labelShort = ''; |
3425
|
|
|
if ($mode == 'opened') { |
3426
|
|
|
$delay_warning = $conf->propal->cloture->warning_delay; |
3427
|
|
|
$status = self::STATUS_VALIDATED; |
3428
|
|
|
$label = $langs->transnoentitiesnoconv("PropalsToClose"); |
3429
|
|
|
$labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse"); |
3430
|
|
|
} |
3431
|
|
|
if ($mode == 'signed') { |
3432
|
|
|
$delay_warning = $conf->propal->facturation->warning_delay; |
3433
|
|
|
$status = self::STATUS_SIGNED; |
3434
|
|
|
$label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered |
3435
|
|
|
$labelShort = $langs->trans("ToBill"); |
3436
|
|
|
} |
3437
|
|
|
|
3438
|
|
|
$response = new WorkboardResponse(); |
3439
|
|
|
$response->warning_delay = $delay_warning / 60 / 60 / 24; |
3440
|
|
|
$response->label = $label; |
3441
|
|
|
$response->labelShort = $labelShort; |
3442
|
|
|
$response->url = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals'; |
3443
|
|
|
$response->url_late = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc'; |
3444
|
|
|
$response->img = img_object('', "propal"); |
3445
|
|
|
|
3446
|
|
|
// This assignment in condition is not a bug. It allows walking the results. |
3447
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
3448
|
|
|
$response->nbtodo++; |
3449
|
|
|
$response->total += $obj->total_ht; |
3450
|
|
|
|
3451
|
|
|
if ($mode == 'opened') { |
3452
|
|
|
$datelimit = $this->db->jdate($obj->datefin); |
3453
|
|
|
if ($datelimit < ($now - $delay_warning)) { |
3454
|
|
|
$response->nbtodolate++; |
3455
|
|
|
} |
3456
|
|
|
} |
3457
|
|
|
// TODO Definir regle des propales a facturer en retard |
3458
|
|
|
// if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++; |
3459
|
|
|
} |
3460
|
|
|
|
3461
|
|
|
return $response; |
3462
|
|
|
} else { |
3463
|
|
|
$this->error = $this->db->error(); |
3464
|
|
|
return -1; |
3465
|
|
|
} |
3466
|
|
|
} |
3467
|
|
|
|
3468
|
|
|
/** |
3469
|
|
|
* Initialise an instance with random values. |
3470
|
|
|
* Used to build previews or test instances. |
3471
|
|
|
* id must be 0 if object instance is a specimen. |
3472
|
|
|
* |
3473
|
|
|
* @return int |
3474
|
|
|
*/ |
3475
|
|
|
public function initAsSpecimen() |
3476
|
|
|
{ |
3477
|
|
|
global $conf, $langs; |
3478
|
|
|
|
3479
|
|
|
// Load array of products prodids |
3480
|
|
|
$num_prods = 0; |
3481
|
|
|
$prodids = array(); |
3482
|
|
|
$sql = "SELECT rowid"; |
3483
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "product"; |
3484
|
|
|
$sql .= " WHERE entity IN (" . getEntity('product') . ")"; |
3485
|
|
|
$sql .= $this->db->plimit(100); |
3486
|
|
|
|
3487
|
|
|
$resql = $this->db->query($sql); |
3488
|
|
|
if ($resql) { |
3489
|
|
|
$num_prods = $this->db->num_rows($resql); |
3490
|
|
|
$i = 0; |
3491
|
|
|
while ($i < $num_prods) { |
3492
|
|
|
$i++; |
3493
|
|
|
$row = $this->db->fetch_row($resql); |
3494
|
|
|
$prodids[$i] = $row[0]; |
3495
|
|
|
} |
3496
|
|
|
} |
3497
|
|
|
|
3498
|
|
|
// Initialise parameters |
3499
|
|
|
$this->id = 0; |
3500
|
|
|
$this->ref = 'SPECIMEN'; |
3501
|
|
|
$this->ref_client = 'NEMICEPS'; |
3502
|
|
|
$this->specimen = 1; |
3503
|
|
|
$this->socid = 1; |
3504
|
|
|
$this->date = time(); |
3505
|
|
|
$this->fin_validite = $this->date + 3600 * 24 * 30; |
3506
|
|
|
$this->cond_reglement_id = 1; |
3507
|
|
|
$this->cond_reglement_code = 'RECEP'; |
3508
|
|
|
$this->mode_reglement_id = 7; |
3509
|
|
|
$this->mode_reglement_code = 'CHQ'; |
3510
|
|
|
$this->availability_id = 1; |
3511
|
|
|
$this->availability_code = 'AV_NOW'; |
3512
|
|
|
$this->demand_reason_id = 1; |
3513
|
|
|
$this->demand_reason_code = 'SRC_00'; |
3514
|
|
|
$this->note_public = 'This is a comment (public)'; |
3515
|
|
|
$this->note_private = 'This is a comment (private)'; |
3516
|
|
|
|
3517
|
|
|
$this->multicurrency_tx = 1; |
3518
|
|
|
$this->multicurrency_code = $conf->currency; |
3519
|
|
|
|
3520
|
|
|
// Lines |
3521
|
|
|
$nbp = 5; |
3522
|
|
|
$xnbp = 0; |
3523
|
|
|
while ($xnbp < $nbp) { |
3524
|
|
|
$line = new PropaleLigne($this->db); |
3525
|
|
|
$line->desc = $langs->trans("Description") . " " . $xnbp; |
3526
|
|
|
$line->qty = 1; |
3527
|
|
|
$line->subprice = 100; |
3528
|
|
|
$line->price = 100; |
3529
|
|
|
$line->tva_tx = 20; |
3530
|
|
|
$line->localtax1_tx = 0; |
3531
|
|
|
$line->localtax2_tx = 0; |
3532
|
|
|
if ($xnbp == 2) { |
3533
|
|
|
$line->total_ht = 50; |
3534
|
|
|
$line->total_ttc = 60; |
3535
|
|
|
$line->total_tva = 10; |
3536
|
|
|
$line->remise_percent = 50; |
3537
|
|
|
} else { |
3538
|
|
|
$line->total_ht = 100; |
3539
|
|
|
$line->total_ttc = 120; |
3540
|
|
|
$line->total_tva = 20; |
3541
|
|
|
$line->remise_percent = 0; |
3542
|
|
|
} |
3543
|
|
|
|
3544
|
|
|
if ($num_prods > 0) { |
3545
|
|
|
$prodid = mt_rand(1, $num_prods); |
3546
|
|
|
$line->fk_product = $prodids[$prodid]; |
3547
|
|
|
$line->product_ref = 'SPECIMEN'; |
3548
|
|
|
} |
3549
|
|
|
|
3550
|
|
|
$this->lines[$xnbp] = $line; |
3551
|
|
|
|
3552
|
|
|
$this->total_ht += $line->total_ht; |
3553
|
|
|
$this->total_tva += $line->total_tva; |
3554
|
|
|
$this->total_ttc += $line->total_ttc; |
3555
|
|
|
|
3556
|
|
|
$xnbp++; |
3557
|
|
|
} |
3558
|
|
|
|
3559
|
|
|
return 1; |
3560
|
|
|
} |
3561
|
|
|
|
3562
|
|
|
/** |
3563
|
|
|
* Load the indicators this->nb for the state board |
3564
|
|
|
* |
3565
|
|
|
* @return int Return integer <0 if ko, >0 if ok |
3566
|
|
|
*/ |
3567
|
|
|
public function loadStateBoard() |
3568
|
|
|
{ |
3569
|
|
|
global $user; |
3570
|
|
|
|
3571
|
|
|
$this->nb = array(); |
3572
|
|
|
$clause = "WHERE"; |
3573
|
|
|
|
3574
|
|
|
$sql = "SELECT count(p.rowid) as nb"; |
3575
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "propal as p"; |
3576
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid"; |
3577
|
|
|
$sql .= " " . $clause . " p.entity IN (" . getEntity('propal') . ")"; |
3578
|
|
|
|
3579
|
|
|
// If the internal user must only see his customers, force searching by him |
3580
|
|
|
$search_sale = 0; |
3581
|
|
|
if (!$user->hasRight('societe', 'client', 'voir')) { |
3582
|
|
|
$search_sale = $user->id; |
3583
|
|
|
} |
3584
|
|
|
// Search on sale representative |
3585
|
|
|
if ($search_sale && $search_sale != '-1') { |
3586
|
|
|
if ($search_sale == -2) { |
3587
|
|
|
$sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)"; |
3588
|
|
|
} elseif ($search_sale > 0) { |
3589
|
|
|
$sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = " . ((int)$search_sale) . ")"; |
3590
|
|
|
} |
3591
|
|
|
} |
3592
|
|
|
|
3593
|
|
|
$resql = $this->db->query($sql); |
3594
|
|
|
if ($resql) { |
3595
|
|
|
// This assignment in condition is not a bug. It allows walking the results. |
3596
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
3597
|
|
|
$this->nb["proposals"] = $obj->nb; |
3598
|
|
|
} |
3599
|
|
|
$this->db->free($resql); |
3600
|
|
|
return 1; |
3601
|
|
|
} else { |
3602
|
|
|
dol_print_error($this->db); |
3603
|
|
|
$this->error = $this->db->error(); |
3604
|
|
|
return -1; |
3605
|
|
|
} |
3606
|
|
|
} |
3607
|
|
|
|
3608
|
|
|
/** |
3609
|
|
|
* getTooltipContentArray |
3610
|
|
|
* @param array $params params to construct tooltip data |
3611
|
|
|
* @return array |
3612
|
|
|
* @since v18 |
3613
|
|
|
*/ |
3614
|
|
|
public function getTooltipContentArray($params) |
3615
|
|
|
{ |
3616
|
|
|
global $conf, $langs, $user; |
3617
|
|
|
|
3618
|
|
|
$langs->load('propal'); |
3619
|
|
|
$datas = []; |
3620
|
|
|
$nofetch = !empty($params['nofetch']); |
3621
|
|
|
|
3622
|
|
|
if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) { |
3623
|
|
|
return ['optimize' => $langs->trans("Proposal")]; |
3624
|
|
|
} |
3625
|
|
|
if ($user->hasRight('propal', 'lire')) { |
3626
|
|
|
$datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("Proposal") . '</u>'; |
3627
|
|
|
if (isset($this->status)) { |
3628
|
|
|
$datas['status'] = ' ' . $this->getLibStatut(5); |
3629
|
|
|
} |
3630
|
|
|
if (!empty($this->ref)) { |
3631
|
|
|
$datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref; |
3632
|
|
|
} |
3633
|
|
|
if (!$nofetch) { |
3634
|
|
|
$langs->load('companies'); |
3635
|
|
|
if (empty($this->thirdparty)) { |
3636
|
|
|
$this->fetch_thirdparty(); |
3637
|
|
|
} |
3638
|
|
|
$datas['customer'] = '<br><b>' . $langs->trans('Customer') . ':</b> ' . $this->thirdparty->getNomUrl(1, '', 0, 1); |
3639
|
|
|
} |
3640
|
|
|
if (!empty($this->ref_customer)) { |
3641
|
|
|
$datas['refcustomer'] = '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_customer; |
3642
|
|
|
} |
3643
|
|
|
if (!$nofetch) { |
3644
|
|
|
$langs->load('project'); |
3645
|
|
|
if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) { |
3646
|
|
|
$res = $this->fetch_project(); |
3647
|
|
|
if ($res > 0 && $this->project instanceof Project) { |
3648
|
|
|
$datas['project'] = '<br><b>' . $langs->trans('Project') . ':</b> ' . $this->project->getNomUrl(1, '', 0, 1); |
3649
|
|
|
} |
3650
|
|
|
} |
3651
|
|
|
} |
3652
|
|
|
if (!empty($this->total_ht)) { |
3653
|
|
|
$datas['amountht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency); |
3654
|
|
|
} |
3655
|
|
|
if (!empty($this->total_tva)) { |
3656
|
|
|
$datas['vat'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency); |
3657
|
|
|
} |
3658
|
|
|
if (!empty($this->total_ttc)) { |
3659
|
|
|
$datas['amountttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency); |
3660
|
|
|
} |
3661
|
|
|
if (!empty($this->date)) { |
3662
|
|
|
$datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day'); |
3663
|
|
|
} |
3664
|
|
|
if (!empty($this->delivery_date)) { |
3665
|
|
|
$datas['deliverydate'] = '<br><b>' . $langs->trans('DeliveryDate') . ':</b> ' . dol_print_date($this->delivery_date, 'dayhour'); |
3666
|
|
|
} |
3667
|
|
|
} |
3668
|
|
|
|
3669
|
|
|
return $datas; |
3670
|
|
|
} |
3671
|
|
|
|
3672
|
|
|
/** |
3673
|
|
|
* Return label of status of proposal (draft, validated, ...) |
3674
|
|
|
* |
3675
|
|
|
* @param int $mode 0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto |
3676
|
|
|
* @return string Label |
3677
|
|
|
*/ |
3678
|
|
|
public function getLibStatut($mode = 0) |
3679
|
|
|
{ |
3680
|
|
|
return $this->LibStatut($this->statut, $mode); |
3681
|
|
|
} |
3682
|
|
|
|
3683
|
|
|
/** |
3684
|
|
|
* Return label of a status (draft, validated, ...) |
3685
|
|
|
* |
3686
|
|
|
* @param int $status Id status |
3687
|
|
|
* @param int $mode 0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto |
3688
|
|
|
* @return string Label |
3689
|
|
|
*/ |
3690
|
|
|
public function LibStatut($status, $mode = 1) |
3691
|
|
|
{ |
3692
|
|
|
// phpcs:enable |
3693
|
|
|
global $hookmanager; |
3694
|
|
|
|
3695
|
|
|
// Init/load array of translation of status |
3696
|
|
|
if (empty($this->labelStatus) || empty($this->labelStatusShort)) { |
3697
|
|
|
global $langs; |
3698
|
|
|
$langs->load("propal"); |
3699
|
|
|
$this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled"); |
3700
|
|
|
$this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft"); |
3701
|
|
|
$this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated"); |
3702
|
|
|
$this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned"); |
3703
|
|
|
$this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned"); |
3704
|
|
|
$this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled"); |
3705
|
|
|
$this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort"); |
3706
|
|
|
$this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort"); |
3707
|
|
|
$this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort"); |
3708
|
|
|
$this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort"); |
3709
|
|
|
$this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort"); |
3710
|
|
|
$this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort"); |
3711
|
|
|
} |
3712
|
|
|
|
3713
|
|
|
$statusType = ''; |
3714
|
|
|
if ($status == self::STATUS_CANCELED) { |
3715
|
|
|
$statusType = 'status9'; |
3716
|
|
|
} elseif ($status == self::STATUS_DRAFT) { |
3717
|
|
|
$statusType = 'status0'; |
3718
|
|
|
} elseif ($status == self::STATUS_VALIDATED) { |
3719
|
|
|
$statusType = 'status1'; |
3720
|
|
|
} elseif ($status == self::STATUS_SIGNED) { |
3721
|
|
|
$statusType = 'status4'; |
3722
|
|
|
} elseif ($status == self::STATUS_NOTSIGNED) { |
3723
|
|
|
$statusType = 'status9'; |
3724
|
|
|
} elseif ($status == self::STATUS_BILLED) { |
3725
|
|
|
$statusType = 'status6'; |
3726
|
|
|
} |
3727
|
|
|
|
3728
|
|
|
$parameters = array('status' => $status, 'mode' => $mode); |
3729
|
|
|
$reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook |
3730
|
|
|
|
3731
|
|
|
if ($reshook > 0) { |
3732
|
|
|
return $hookmanager->resPrint; |
3733
|
|
|
} |
3734
|
|
|
|
3735
|
|
|
return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); |
3736
|
|
|
} |
3737
|
|
|
|
3738
|
|
|
/** |
3739
|
|
|
* Return clicable link of object (with eventually picto) |
3740
|
|
|
* |
3741
|
|
|
* @param int $withpicto Add picto into link |
3742
|
|
|
* @param string $option Where point the link ('expedition', 'document', ...) |
3743
|
|
|
* @param string $get_params Parameters added to url |
3744
|
|
|
* @param int $notooltip 1=Disable tooltip |
3745
|
|
|
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking |
3746
|
|
|
* @param int $addlinktonotes -1=Disable, 0=Just add label show notes, 1=Add private note (only internal user), 2=Add public note (internal or external user), 3=Add private (internal user) and public note (internal and external user) |
3747
|
|
|
* @return string String with URL |
3748
|
|
|
*/ |
3749
|
|
|
public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1) |
3750
|
|
|
{ |
3751
|
|
|
global $langs, $conf, $user, $hookmanager; |
3752
|
|
|
|
3753
|
|
|
if (!empty($conf->dol_no_mouse_hover)) { |
3754
|
|
|
$notooltip = 1; // Force disable tooltips |
3755
|
|
|
} |
3756
|
|
|
|
3757
|
|
|
$result = ''; |
3758
|
|
|
$params = [ |
3759
|
|
|
'id' => $this->id, |
3760
|
|
|
'objecttype' => $this->element, |
3761
|
|
|
'option' => $option, |
3762
|
|
|
'nofetch' => 1, |
3763
|
|
|
]; |
3764
|
|
|
$classfortooltip = 'classfortooltip'; |
3765
|
|
|
$dataparams = ''; |
3766
|
|
|
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { |
3767
|
|
|
$classfortooltip = 'classforajaxtooltip'; |
3768
|
|
|
$dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"'; |
3769
|
|
|
$label = ''; |
3770
|
|
|
} else { |
3771
|
|
|
$label = implode($this->getTooltipContentArray($params)); |
3772
|
|
|
} |
3773
|
|
|
|
3774
|
|
|
$url = ''; |
3775
|
|
|
if ($user->hasRight('propal', 'lire')) { |
3776
|
|
|
if ($option == '') { |
3777
|
|
|
$url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params; |
3778
|
|
|
} elseif ($option == 'compta') { // deprecated |
3779
|
|
|
$url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params; |
3780
|
|
|
} elseif ($option == 'expedition') { |
3781
|
|
|
$url = constant('BASE_URL') . '/expedition/propal.php?id=' . $this->id . $get_params; |
3782
|
|
|
} elseif ($option == 'document') { |
3783
|
|
|
$url = constant('BASE_URL') . '/comm/propal/document.php?id=' . $this->id . $get_params; |
3784
|
|
|
} |
3785
|
|
|
|
3786
|
|
|
if ($option != 'nolink') { |
3787
|
|
|
// Add param to save lastsearch_values or not |
3788
|
|
|
$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); |
3789
|
|
|
if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { |
3790
|
|
|
$add_save_lastsearch_values = 1; |
3791
|
|
|
} |
3792
|
|
|
if ($add_save_lastsearch_values) { |
3793
|
|
|
$url .= '&save_lastsearch_values=1'; |
3794
|
|
|
} |
3795
|
|
|
} |
3796
|
|
|
} |
3797
|
|
|
|
3798
|
|
|
$linkclose = ''; |
3799
|
|
|
if (empty($notooltip) && $user->hasRight('propal', 'lire')) { |
3800
|
|
|
if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) { |
3801
|
|
|
$label = $langs->trans("Proposal"); |
3802
|
|
|
$linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"'; |
3803
|
|
|
} |
3804
|
|
|
$linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"'); |
3805
|
|
|
$linkclose .= $dataparams . ' class="' . $classfortooltip . '"'; |
3806
|
|
|
} |
3807
|
|
|
|
3808
|
|
|
$linkstart = '<a href="' . $url . '"'; |
3809
|
|
|
$linkstart .= $linkclose . '>'; |
3810
|
|
|
$linkend = '</a>'; |
3811
|
|
|
|
3812
|
|
|
$result .= $linkstart; |
3813
|
|
|
if ($withpicto) { |
3814
|
|
|
$result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1); |
3815
|
|
|
} |
3816
|
|
|
if ($withpicto != 2) { |
3817
|
|
|
$result .= $this->ref; |
3818
|
|
|
} |
3819
|
|
|
$result .= $linkend; |
3820
|
|
|
|
3821
|
|
|
if ($addlinktonotes >= 0) { |
3822
|
|
|
$txttoshow = ''; |
3823
|
|
|
|
3824
|
|
|
if ($addlinktonotes == 0) { |
3825
|
|
|
if (!empty($this->note_private) || !empty($this->note_public)) { |
3826
|
|
|
$txttoshow = $langs->trans('ViewPrivateNote'); |
3827
|
|
|
} |
3828
|
|
|
} elseif ($addlinktonotes == 1) { |
3829
|
|
|
if (!empty($this->note_private)) { |
3830
|
|
|
$txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1)); |
3831
|
|
|
} |
3832
|
|
|
} elseif ($addlinktonotes == 2) { |
3833
|
|
|
if (!empty($this->note_public)) { |
3834
|
|
|
$txttoshow .= dol_string_nohtmltag($this->note_public, 1); |
3835
|
|
|
} |
3836
|
|
|
} elseif ($addlinktonotes == 3) { |
3837
|
|
|
if ($user->socid > 0) { |
3838
|
|
|
if (!empty($this->note_public)) { |
3839
|
|
|
$txttoshow .= dol_string_nohtmltag($this->note_public, 1); |
3840
|
|
|
} |
3841
|
|
|
} else { |
3842
|
|
|
if (!empty($this->note_public)) { |
3843
|
|
|
$txttoshow .= dol_string_nohtmltag($this->note_public, 1); |
3844
|
|
|
} |
3845
|
|
|
if (!empty($this->note_private)) { |
3846
|
|
|
if (!empty($txttoshow)) { |
3847
|
|
|
$txttoshow .= '<br><br>'; |
3848
|
|
|
} |
3849
|
|
|
$txttoshow .= dol_string_nohtmltag($this->note_private, 1); |
3850
|
|
|
} |
3851
|
|
|
} |
3852
|
|
|
} |
3853
|
|
|
|
3854
|
|
|
if ($txttoshow) { |
3855
|
|
|
$result .= ' <span class="note inline-block">'; |
3856
|
|
|
$result .= '<a href="' . constant('BASE_URL') . '/comm/propal/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($txttoshow) . '">'; |
3857
|
|
|
$result .= img_picto('', 'note'); |
3858
|
|
|
$result .= '</a>'; |
3859
|
|
|
$result .= '</span>'; |
3860
|
|
|
} |
3861
|
|
|
} |
3862
|
|
|
|
3863
|
|
|
global $action; |
3864
|
|
|
$hookmanager->initHooks(array($this->element . 'dao')); |
3865
|
|
|
$parameters = array('id' => $this->id, 'getnomurl' => &$result); |
3866
|
|
|
$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks |
3867
|
|
|
if ($reshook > 0) { |
3868
|
|
|
$result = $hookmanager->resPrint; |
3869
|
|
|
} else { |
3870
|
|
|
$result .= $hookmanager->resPrint; |
3871
|
|
|
} |
3872
|
|
|
return $result; |
3873
|
|
|
} |
3874
|
|
|
|
3875
|
|
|
/** |
3876
|
|
|
* Retrieve an array of proposal lines |
3877
|
|
|
* |
3878
|
|
|
* @param string $sqlforgedfilters Filter on other fields |
3879
|
|
|
* @return int >0 if OK, <0 if KO |
3880
|
|
|
*/ |
3881
|
|
|
public function getLinesArray($sqlforgedfilters = '') |
3882
|
|
|
{ |
3883
|
|
|
return $this->fetch_lines(0, 0, $sqlforgedfilters); |
3884
|
|
|
} |
3885
|
|
|
|
3886
|
|
|
/** |
3887
|
|
|
* Return clicable link of object (with eventually picto) |
3888
|
|
|
* |
3889
|
|
|
* @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) |
3890
|
|
|
* @param array $arraydata Array of data |
3891
|
|
|
* @return string HTML Code for Kanban thumb. |
3892
|
|
|
*/ |
3893
|
|
|
public function getKanbanView($option = '', $arraydata = null) |
3894
|
|
|
{ |
3895
|
|
|
global $langs; |
3896
|
|
|
|
3897
|
|
|
$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); |
3898
|
|
|
|
3899
|
|
|
$return = '<div class="box-flex-item box-flex-grow-zero">'; |
3900
|
|
|
$return .= '<div class="info-box info-box-sm">'; |
3901
|
|
|
$return .= '<div class="info-box-icon bg-infobox-action">'; |
3902
|
|
|
$return .= img_picto('', $this->picto); |
3903
|
|
|
$return .= '</div>'; |
3904
|
|
|
$return .= '<div class="info-box-content">'; |
3905
|
|
|
$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>'; |
3906
|
|
|
if ($selected >= 0) { |
3907
|
|
|
$return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>'; |
3908
|
|
|
} |
3909
|
|
|
if (!empty($arraydata['projectlink'])) { |
3910
|
|
|
$return .= '<span class="info-box-ref"> | ' . $arraydata['projectlink'] . '</span>'; |
3911
|
|
|
} |
3912
|
|
|
$return .= '<br>'; |
3913
|
|
|
if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) { |
3914
|
|
|
$return .= '<div class="info-box-ref tdoverflowmax150">' . $this->thirdparty->getNomUrl(1) . '</div>'; |
3915
|
|
|
} |
3916
|
|
|
if (property_exists($this, 'total_ht')) { |
3917
|
|
|
$return .= '<span class="info-box-label amount" title="' . $langs->trans("AmountHT") . '">' . price($this->total_ht) . '</span>'; |
3918
|
|
|
} |
3919
|
|
|
if (!empty($arraydata['authorlink'])) { |
3920
|
|
|
$return .= ' <span class="info-box-label">' . $arraydata['authorlink'] . '</span>'; |
3921
|
|
|
} |
3922
|
|
|
if (method_exists($this, 'getLibStatut')) { |
3923
|
|
|
$return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>'; |
3924
|
|
|
} |
3925
|
|
|
$return .= '</div>'; |
3926
|
|
|
$return .= '</div>'; |
3927
|
|
|
$return .= '</div>'; |
3928
|
|
|
return $return; |
3929
|
|
|
} |
3930
|
|
|
} |
3931
|
|
|
|
This property has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.