dolibarr  13.0.2
api_proposals.class.php
1 <?php
2 /* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3  * Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2020 Thibault FOUCART <support@ptibogxiv.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 use Luracast\Restler\RestException;
21 
22 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
23 
24 
31 class Proposals extends DolibarrApi
32 {
36  static $FIELDS = array(
37  'socid'
38  );
39 
43  public $propal;
44 
48  public function __construct()
49  {
50  global $db, $conf;
51  $this->db = $db;
52  $this->propal = new Propal($this->db);
53  }
54 
66  public function get($id, $contact_list = 1)
67  {
68  return $this->_fetch($id, '', '', $contact_list);
69  }
70 
84  public function getByRef($ref, $contact_list = 1)
85  {
86  return $this->_fetch('', $ref, '', $contact_list);
87  }
88 
102  public function getByRefExt($ref_ext, $contact_list = 1)
103  {
104  return $this->_fetch('', '', $ref_ext, $contact_list);
105  }
106 
120  private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
121  {
122  if (!DolibarrApiAccess::$user->rights->propal->lire) {
123  throw new RestException(401);
124  }
125 
126  $result = $this->propal->fetch($id, $ref, $ref_ext);
127  if (!$result) {
128  throw new RestException(404, 'Commercial Proposal not found');
129  }
130 
131  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
132  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
133  }
134 
135  // Add external contacts ids.
136  $this->propal->contacts_ids = $this->propal->liste_contact(-1, 'external', $contact_list);
137  $this->propal->fetchObjectLinked();
138  return $this->_cleanObjectDatas($this->propal);
139  }
140 
154  public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $sqlfilters = '')
155  {
156  global $db, $conf;
157 
158  $obj_ret = array();
159 
160  // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
161  $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
162 
163  // If the internal user must only see his customers, force searching by him
164  $search_sale = 0;
165  if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) $search_sale = DolibarrApiAccess::$user->id;
166 
167  $sql = "SELECT t.rowid";
168  if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
169  $sql .= " FROM ".MAIN_DB_PREFIX."propal as t";
170 
171  if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
172 
173  $sql .= ' WHERE t.entity IN ('.getEntity('propal').')';
174  if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= " AND t.fk_soc = sc.fk_soc";
175  if ($socids) $sql .= " AND t.fk_soc IN (".$socids.")";
176  if ($search_sale > 0) $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
177  // Insert sale filter
178  if ($search_sale > 0)
179  {
180  $sql .= " AND sc.fk_user = ".$search_sale;
181  }
182  // Add sql filters
183  if ($sqlfilters)
184  {
185  if (!DolibarrApi::_checkFilters($sqlfilters))
186  {
187  throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
188  }
189  $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
190  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
191  }
192 
193  $sql .= $this->db->order($sortfield, $sortorder);
194  if ($limit) {
195  if ($page < 0)
196  {
197  $page = 0;
198  }
199  $offset = $limit * $page;
200 
201  $sql .= $this->db->plimit($limit + 1, $offset);
202  }
203 
204  dol_syslog("API Rest request");
205  $result = $this->db->query($sql);
206 
207  if ($result)
208  {
209  $num = $this->db->num_rows($result);
210  $min = min($num, ($limit <= 0 ? $num : $limit));
211  $i = 0;
212  while ($i < $min)
213  {
214  $obj = $this->db->fetch_object($result);
215  $proposal_static = new Propal($this->db);
216  if ($proposal_static->fetch($obj->rowid)) {
217  // Add external contacts ids
218  $proposal_static->contacts_ids = $proposal_static->liste_contact(-1, 'external', 1);
219  $obj_ret[] = $this->_cleanObjectDatas($proposal_static);
220  }
221  $i++;
222  }
223  } else {
224  throw new RestException(503, 'Error when retrieve propal list : '.$this->db->lasterror());
225  }
226  if (!count($obj_ret)) {
227  throw new RestException(404, 'No proposal found');
228  }
229  return $obj_ret;
230  }
231 
238  public function post($request_data = null)
239  {
240  if (!DolibarrApiAccess::$user->rights->propal->creer) {
241  throw new RestException(401, "Insuffisant rights");
242  }
243  // Check mandatory fields
244  $result = $this->_validate($request_data);
245 
246  foreach ($request_data as $field => $value) {
247  $this->propal->$field = $value;
248  }
249  /*if (isset($request_data["lines"])) {
250  $lines = array();
251  foreach ($request_data["lines"] as $line) {
252  array_push($lines, (object) $line);
253  }
254  $this->propal->lines = $lines;
255  }*/
256  if ($this->propal->create(DolibarrApiAccess::$user) < 0) {
257  throw new RestException(500, "Error creating order", array_merge(array($this->propal->error), $this->propal->errors));
258  }
259 
260  return $this->propal->id;
261  }
262 
272  public function getLines($id)
273  {
274  if (!DolibarrApiAccess::$user->rights->propal->lire) {
275  throw new RestException(401);
276  }
277 
278  $result = $this->propal->fetch($id);
279  if (!$result) {
280  throw new RestException(404, 'Commercial Proposal not found');
281  }
282 
283  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
284  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
285  }
286  $this->propal->getLinesArray();
287  $result = array();
288  foreach ($this->propal->lines as $line) {
289  array_push($result, $this->_cleanObjectDatas($line));
290  }
291  return $result;
292  }
293 
304  public function postLine($id, $request_data = null)
305  {
306  if (!DolibarrApiAccess::$user->rights->propal->creer) {
307  throw new RestException(401);
308  }
309 
310  $result = $this->propal->fetch($id);
311  if (!$result) {
312  throw new RestException(404, 'Commercial Proposal not found');
313  }
314 
315  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
316  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
317  }
318 
319  $request_data = (object) $request_data;
320 
321  $updateRes = $this->propal->addline(
322  $request_data->desc,
323  $request_data->subprice,
324  $request_data->qty,
325  $request_data->tva_tx,
326  $request_data->localtax1_tx,
327  $request_data->localtax2_tx,
328  $request_data->fk_product,
329  $request_data->remise_percent,
330  'HT',
331  0,
332  $request_data->info_bits,
333  $request_data->product_type,
334  $request_data->rang,
335  $request_data->special_code,
336  $request_data->fk_parent_line,
337  $request_data->fk_fournprice,
338  $request_data->pa_ht,
339  $request_data->label,
340  $request_data->date_start,
341  $request_data->date_end,
342  $request_data->array_options,
343  $request_data->fk_unit,
344  $request_data->origin,
345  $request_data->origin_id,
346  $request_data->multicurrency_subprice,
347  $request_data->fk_remise_except
348  );
349 
350  if ($updateRes > 0) {
351  return $updateRes;
352  } else {
353  throw new RestException(400, $this->propal->error);
354  }
355  }
356 
368  public function putLine($id, $lineid, $request_data = null)
369  {
370  if (!DolibarrApiAccess::$user->rights->propal->creer) {
371  throw new RestException(401);
372  }
373 
374  $result = $this->propal->fetch($id);
375  if ($result <= 0) {
376  throw new RestException(404, 'Proposal not found');
377  }
378 
379  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
380  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
381  }
382 
383  $request_data = (object) $request_data;
384 
385  $propalline = new PropaleLigne($this->db);
386  $result = $propalline->fetch($lineid);
387  if ($result <= 0) {
388  throw new RestException(404, 'Proposal line not found');
389  }
390 
391  $updateRes = $this->propal->updateline(
392  $lineid,
393  isset($request_data->subprice) ? $request_data->subprice : $propalline->subprice,
394  isset($request_data->qty) ? $request_data->qty : $propalline->qty,
395  isset($request_data->remise_percent) ? $request_data->remise_percent : $propalline->remise_percent,
396  isset($request_data->tva_tx) ? $request_data->tva_tx : $propalline->tva_tx,
397  isset($request_data->localtax1_tx) ? $request_data->localtax1_tx : $propalline->localtax1_tx,
398  isset($request_data->localtax2_tx) ? $request_data->localtax2_tx : $propalline->localtax2_tx,
399  isset($request_data->desc) ? $request_data->desc : $propalline->desc,
400  'HT',
401  isset($request_data->info_bits) ? $request_data->info_bits : $propalline->info_bits,
402  isset($request_data->special_code) ? $request_data->special_code : $propalline->special_code,
403  isset($request_data->fk_parent_line) ? $request_data->fk_parent_line : $propalline->fk_parent_line,
404  0,
405  isset($request_data->fk_fournprice) ? $request_data->fk_fournprice : $propalline->fk_fournprice,
406  isset($request_data->pa_ht) ? $request_data->pa_ht : $propalline->pa_ht,
407  isset($request_data->label) ? $request_data->label : $propalline->label,
408  isset($request_data->product_type) ? $request_data->product_type : $propalline->product_type,
409  isset($request_data->date_start) ? $request_data->date_start : $propalline->date_start,
410  isset($request_data->date_end) ? $request_data->date_end : $propalline->date_end,
411  isset($request_data->array_options) ? $request_data->array_options : $propalline->array_options,
412  isset($request_data->fk_unit) ? $request_data->fk_unit : $propalline->fk_unit,
413  isset($request_data->multicurrency_subprice) ? $request_data->multicurrency_subprice : $propalline->subprice
414  );
415 
416  if ($updateRes > 0) {
417  $result = $this->get($id);
418  unset($result->line);
419  return $this->_cleanObjectDatas($result);
420  }
421  return false;
422  }
423 
438  public function deleteLine($id, $lineid)
439  {
440  if (!DolibarrApiAccess::$user->rights->propal->creer) {
441  throw new RestException(401);
442  }
443 
444  $result = $this->propal->fetch($id);
445  if (!$result) {
446  throw new RestException(404, 'Proposal not found');
447  }
448 
449  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
450  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
451  }
452 
453  // TODO Check the lineid $lineid is a line of ojbect
454 
455  $updateRes = $this->propal->deleteline($lineid);
456  if ($updateRes > 0) {
457  return $this->get($id);
458  } else {
459  throw new RestException(405, $this->propal->error);
460  }
461  }
462 
477  public function postContact($id, $contactid, $type)
478  {
479  if (!DolibarrApiAccess::$user->rights->propal->creer) {
480  throw new RestException(401);
481  }
482 
483  $result = $this->propal->fetch($id);
484 
485  if (!$result) {
486  throw new RestException(404, 'Proposal not found');
487  }
488 
489  if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
490  throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
491  }
492 
493  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
494  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
495  }
496 
497  $result = $this->propal->add_contact($contactid, $type, 'external');
498 
499  if (!$result) {
500  throw new RestException(500, 'Error when added the contact');
501  }
502 
503  return $this->propal;
504  }
505 
521  public function deleteContact($id, $contactid, $type)
522  {
523  if (!DolibarrApiAccess::$user->rights->propal->creer) {
524  throw new RestException(401);
525  }
526 
527  $result = $this->propal->fetch($id);
528 
529  if (!$result) {
530  throw new RestException(404, 'Proposal not found');
531  }
532 
533  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
534  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
535  }
536 
537  $contacts = $this->invoice->liste_contact();
538 
539  foreach ($contacts as $contact) {
540  if ($contact['id'] == $contactid && $contact['code'] == $type) {
541  $result = $this->propal->delete_contact($contact['rowid']);
542 
543  if (!$result) {
544  throw new RestException(500, 'Error when deleted the contact');
545  }
546  }
547  }
548 
549  return $this->_cleanObjectDatas($this->propal);
550  }
551 
560  public function put($id, $request_data = null)
561  {
562  if (!DolibarrApiAccess::$user->rights->propal->creer) {
563  throw new RestException(401);
564  }
565 
566  $result = $this->propal->fetch($id);
567  if (!$result) {
568  throw new RestException(404, 'Proposal not found');
569  }
570 
571  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
572  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
573  }
574  foreach ($request_data as $field => $value) {
575  if ($field == 'id') continue;
576  $this->propal->$field = $value;
577  }
578 
579  // update end of validity date
580  if (empty($this->propal->fin_validite) && !empty($this->propal->duree_validite) && !empty($this->propal->date_creation))
581  {
582  $this->propal->fin_validite = $this->propal->date_creation + ($this->propal->duree_validite * 24 * 3600);
583  }
584  if (!empty($this->propal->fin_validite))
585  {
586  if ($this->propal->set_echeance(DolibarrApiAccess::$user, $this->propal->fin_validite) < 0)
587  {
588  throw new RestException(500, $this->propal->error);
589  }
590  }
591 
592  if ($this->propal->update(DolibarrApiAccess::$user) > 0)
593  {
594  return $this->get($id);
595  } else {
596  throw new RestException(500, $this->propal->error);
597  }
598  }
599 
607  public function delete($id)
608  {
609  if (!DolibarrApiAccess::$user->rights->propal->supprimer) {
610  throw new RestException(401);
611  }
612  $result = $this->propal->fetch($id);
613  if (!$result) {
614  throw new RestException(404, 'Commercial Proposal not found');
615  }
616 
617  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
618  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
619  }
620 
621  if (!$this->propal->delete(DolibarrApiAccess::$user)) {
622  throw new RestException(500, 'Error when delete Commercial Proposal : '.$this->propal->error);
623  }
624 
625  return array(
626  'success' => array(
627  'code' => 200,
628  'message' => 'Commercial Proposal deleted'
629  )
630  );
631  }
632 
642  public function settodraft($id)
643  {
644  if (!DolibarrApiAccess::$user->rights->propal->creer) {
645  throw new RestException(401);
646  }
647  $result = $this->propal->fetch($id);
648  if (!$result) {
649  throw new RestException(404, 'Proposal not found');
650  }
651 
652  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
653  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
654  }
655 
656  $result = $this->propal->setDraft(DolibarrApiAccess::$user);
657  if ($result == 0) {
658  throw new RestException(304, 'Nothing done. May be object is already draft');
659  }
660  if ($result < 0) {
661  throw new RestException(500, 'Error : '.$this->propal->error);
662  }
663 
664  $result = $this->propal->fetch($id);
665  if (!$result) {
666  throw new RestException(404, 'Proposal not found');
667  }
668 
669  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
670  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
671  }
672 
673  $this->propal->fetchObjectLinked();
674 
675  return $this->_cleanObjectDatas($this->propal);
676  }
677 
678 
699  public function validate($id, $notrigger = 0)
700  {
701  if (!DolibarrApiAccess::$user->rights->propal->creer) {
702  throw new RestException(401);
703  }
704  $result = $this->propal->fetch($id);
705  if (!$result) {
706  throw new RestException(404, 'Commercial Proposal not found');
707  }
708 
709  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
710  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
711  }
712 
713  $result = $this->propal->valid(DolibarrApiAccess::$user, $notrigger);
714  if ($result == 0) {
715  throw new RestException(304, 'Error nothing done. May be object is already validated');
716  }
717  if ($result < 0) {
718  throw new RestException(500, 'Error when validating Commercial Proposal: '.$this->propal->error);
719  }
720 
721  $result = $this->propal->fetch($id);
722  if (!$result) {
723  throw new RestException(404, 'Commercial Proposal not found');
724  }
725 
726  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
727  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
728  }
729 
730  $this->propal->fetchObjectLinked();
731 
732  return $this->_cleanObjectDatas($this->propal);
733  }
734 
747  public function close($id, $status, $note_private = '', $notrigger = 0)
748  {
749  if (!DolibarrApiAccess::$user->rights->propal->creer) {
750  throw new RestException(401);
751  }
752  $result = $this->propal->fetch($id);
753  if (!$result) {
754  throw new RestException(404, 'Commercial Proposal not found');
755  }
756 
757  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
758  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
759  }
760 
761  $result = $this->propal->cloture(DolibarrApiAccess::$user, $status, $note_private, $notrigger);
762  if ($result == 0) {
763  throw new RestException(304, 'Error nothing done. May be object is already closed');
764  }
765  if ($result < 0) {
766  throw new RestException(500, 'Error when closing Commercial Proposal: '.$this->propal->error);
767  }
768 
769  $result = $this->propal->fetch($id);
770  if (!$result) {
771  throw new RestException(404, 'Proposal not found');
772  }
773 
774  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
775  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
776  }
777 
778  $this->propal->fetchObjectLinked();
779 
780  return $this->_cleanObjectDatas($this->propal);
781  }
782 
792  public function setinvoiced($id)
793  {
794  if (!DolibarrApiAccess::$user->rights->propal->creer) {
795  throw new RestException(401);
796  }
797  $result = $this->propal->fetch($id);
798  if (!$result) {
799  throw new RestException(404, 'Commercial Proposal not found');
800  }
801 
802  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
803  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
804  }
805 
806  $result = $this->propal->classifyBilled(DolibarrApiAccess::$user);
807  if ($result < 0) {
808  throw new RestException(500, 'Error : '.$this->propal->error);
809  }
810 
811  $result = $this->propal->fetch($id);
812  if (!$result) {
813  throw new RestException(404, 'Proposal not found');
814  }
815 
816  if (!DolibarrApi::_checkAccessToResource('propal', $this->propal->id)) {
817  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
818  }
819 
820  $this->propal->fetchObjectLinked();
821 
822  return $this->_cleanObjectDatas($this->propal);
823  }
824 
825 
833  private function _validate($data)
834  {
835  $propal = array();
836  foreach (Proposals::$FIELDS as $field) {
837  if (!isset($data[$field]))
838  throw new RestException(400, "$field field missing");
839  $propal[$field] = $data[$field];
840  }
841  return $propal;
842  }
843 
844 
845  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
852  protected function _cleanObjectDatas($object)
853  {
854  // phpcs:enable
855  $object = parent::_cleanObjectDatas($object);
856 
857  unset($object->note);
858  unset($object->name);
859  unset($object->lastname);
860  unset($object->firstname);
861  unset($object->civility_id);
862  unset($object->address);
863 
864  return $object;
865  }
866 }
_validate($data)
Validate fields before create or update object.
getByRefExt($ref_ext, $contact_list=1)
Get properties of an proposal object by ref_ext.
_fetch($id, $ref= '', $ref_ext= '', $contact_list=1)
Get properties of an proposal object.
index($sortfield="t.rowid", $sortorder= 'ASC', $limit=100, $page=0, $thirdparty_ids= '', $sqlfilters= '')
List commercial proposals.
setinvoiced($id)
Set a commercial proposal billed.
getLines($id)
Get lines of a commercial proposal.
$conf db
API class for accounts.
Definition: inc.php:54
_checkFilters($sqlfilters)
Return if a $sqlfilters parameter is valid.
Definition: api.class.php:278
Class for API REST v1.
Definition: api.class.php:30
deleteLine($id, $lineid)
Delete a line of given commercial proposal.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
postContact($id, $contactid, $type)
Add a contact type of given commercial proposal.
postLine($id, $request_data=null)
Add a line to given commercial proposal.
Class to manage commercial proposal lines.
getByRef($ref, $contact_list=1)
Get properties of an proposal object by ref.
putLine($id, $lineid, $request_data=null)
Update a line of given commercial proposal.
close($id, $status, $note_private= '', $notrigger=0)
Close (Accept or refuse) a quote / commercial proposal.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename= '', $feature2= '', $dbt_keyfield= 'fk_soc', $dbt_select= 'rowid')
Check user access to a resource.
Definition: api.class.php:252
post($request_data=null)
Create commercial proposal object.
deleteContact($id, $contactid, $type)
Delete a contact type of given commercial proposal.
put($id, $request_data=null)
Update commercial proposal general fields (won&#39;t touch lines of commercial proposal) ...
_cleanObjectDatas($object)
Clean sensible object datas.
validate($id, $notrigger=0)
Validate a commercial proposal.
Class to manage proposals.
__construct()
Constructor.
settodraft($id)
Set a proposal to draft.