diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index 0dd43b8ad65..9e861d863f9 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -16,6 +16,7 @@ * Copyright (C) 2020 Nicolas ZABOURI * Copyright (C) 2022 Gauthier VERDOL * Copyright (C) 2023 Lenin Rivas + * Copyright (C) 2023 William Mead * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -235,6 +236,16 @@ if (empty($reshook)) { } } } + } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $usercanclose) { + // Cancel proposal + $result = $object->setCancel($user); + if ($result > 0) { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit(); + } else { + $langs->load("errors"); + setEventMessages($object->error, $object->errors, 'errors'); + } } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) { // Delete proposal $result = $object->delete($user); @@ -785,7 +796,7 @@ if (empty($reshook)) { } elseif ($action == 'confirm_reopen' && $usercanclose && !GETPOST('cancel', 'alpha')) { // Reopen proposal // prevent browser refresh from reopening proposal several times - if ($object->statut == Propal::STATUS_SIGNED || $object->statut == Propal::STATUS_NOTSIGNED || $object->statut == Propal::STATUS_BILLED) { + if ($object->statut == Propal::STATUS_SIGNED || $object->statut == Propal::STATUS_NOTSIGNED || $object->statut == Propal::STATUS_BILLED || $object->statut == Propal::STATUS_CANCELED) { $db->begin(); $result = $object->reopen($user, !getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')); @@ -2383,6 +2394,9 @@ if ($action == 'create') { } else { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?statut=3&id=' . $object->id, $langs->trans('Close'), '', 'confirm_closeas', $formquestion, '', 1, 250); } + } elseif ($action == 'cancel') { + // Confirm cancel + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans("CancelPropal"), $langs->trans('ConfirmCancelPropal', $object->ref), 'confirm_cancel', '', 0, 1); } elseif ($action == 'delete') { // Confirm delete $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteProp'), $langs->trans('ConfirmDeleteProp', $object->ref), 'confirm_delete', '', 0, 1); @@ -3005,7 +3019,7 @@ if ($action == 'create') { } // ReOpen - if (((getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && $object->statut == Propal::STATUS_NOTSIGNED) || (!getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && ($object->statut == Propal::STATUS_SIGNED || $object->statut == Propal::STATUS_NOTSIGNED || $object->statut == Propal::STATUS_BILLED))) && $usercanclose) { + if (((getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && $object->statut == Propal::STATUS_NOTSIGNED) || (!getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && ($object->statut == Propal::STATUS_SIGNED || $object->statut == Propal::STATUS_NOTSIGNED || $object->statut == Propal::STATUS_BILLED || $object->statut == Propal::STATUS_CANCELED))) && $usercanclose) { print ''.$langs->trans('ReOpen').''; } @@ -3085,6 +3099,11 @@ if ($action == 'create') { } } + // Cancel propal + if ($object->status > Propal::STATUS_DRAFT && $usercanclose) { + print ''.$langs->trans("CancelPropal").''; + } + // Clone if ($usercancreate) { print ''.$langs->trans("ToClone").''; diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 28d62bafb87..da70e894763 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -18,6 +18,7 @@ * Copyright (C) 2022 ATM Consulting * Copyright (C) 2022 OpenDSI * Copyright (C) 2022 Gauthier VERDOL + * Copyright (C) 2023 William Mead * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -136,14 +137,14 @@ class Propal extends CommonObject * Status of the quote * @var int * @deprecated Try to use $status now - * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED + * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED */ public $statut; /** * Status of the quote * @var int - * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED + * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED */ public $status; @@ -373,6 +374,10 @@ class Propal extends CommonObject ); // END MODULEBUILDER PROPERTIES + /** + * Canceled status + */ + const STATUS_CANCELED = -1; /** * Draft status */ @@ -2859,6 +2864,53 @@ class Propal extends CommonObject } } + /** + * Cancel the proposal + * + * @param User $user Object user + * @return int Return integer if KO <0 , if OK >0 + */ + public function setCancel(User $user) + { + $error = 0; + + $this->db->begin(); + + $sql = "UPDATE ". MAIN_DB_PREFIX . "propal"; + $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ","; + $sql .= " fk_user_modif = " . ((int) $user->id); + $sql .= " WHERE rowid = " . ((int) $this->id); + + dol_syslog(get_class($this)."::cancel", LOG_DEBUG); + if ($this->db->query($sql)) { + if (!$error) { + // Call trigger + $result = $this->call_trigger('PROPAL_CANCEL', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + + if (!$error) { + $this->statut = self::STATUS_CANCELED; + $this->db->commit(); + return 1; + } else { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR); + $this->error .= ($this->error ? ', '.$errmsg : $errmsg); + } + $this->db->rollback(); + return -1; + } + } else { + $this->error = $this->db->error(); + $this->db->rollback(); + return -1; + } + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Set draft status @@ -3410,11 +3462,13 @@ class Propal extends CommonObject if (empty($this->labelStatus) || empty($this->labelStatusShort)) { global $langs; $langs->load("propal"); + $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled"); $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft"); $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated"); $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned"); $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned"); $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled"); + $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort"); $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort"); $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort"); $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort"); @@ -3423,7 +3477,9 @@ class Propal extends CommonObject } $statusType = ''; - if ($status == self::STATUS_DRAFT) { + if ($status == self::STATUS_CANCELED) { + $statusType = 'status9'; + } elseif ($status == self::STATUS_DRAFT) { $statusType = 'status0'; } elseif ($status == self::STATUS_VALIDATED) { $statusType = 'status1'; diff --git a/htdocs/core/lib/profid.lib.php b/htdocs/core/lib/profid.lib.php index 98990266a42..f167a8f3713 100644 --- a/htdocs/core/lib/profid.lib.php +++ b/htdocs/core/lib/profid.lib.php @@ -38,6 +38,8 @@ function isValidSiren($siren) return false; } + // TODO : use the Luhn algorithm instead + // we take each figure one by one and: // - if its index is odd then we double its value, // - if the latter is higher than 9 then we substract 9 from it, @@ -56,3 +58,41 @@ function isValidSiren($siren) // the siren is valid if the sum is a multiple of 10 return (($sum % 10) == 0) ? true : false; } + + +/** + * Check the validity of a SIRET. + * + * @param string $siret SIRET to check + * @return boolean True if valid, False otherwise + */ +function isValidSiret($siret) +{ + $siret = trim($siret); + $siret = preg_replace('/(\s)/', '', $siret); + + if (!is_numeric($siret) || dol_strlen($siret) != 14) { + return false; + } + + // TODO: use the Luhn algorithm instead + // TODO: handle the exception of "La Poste" (356 000 000 #####) + + // we take each figure one by one and: + // - if its index is even then we double its value, + // - if the latter is higher than 9 then we substract 9 from it, + // - and finally we add the result to the overall sum. + $sum = 0; + for ($index = 0; $index < 14; $index++) { + $number = (int) $siret[$index]; + if ($index % 2 == 0) { + if (($number *= 2) > 9) { + $number -= 9; + } + } + $sum += $number; + } + + // the SIRET is valid if the sum is a multiple of 10 + return $sum % 10 == 0; +} diff --git a/htdocs/langs/en_US/propal.lang b/htdocs/langs/en_US/propal.lang index c50f6d27c3c..f97a755dba9 100644 --- a/htdocs/langs/en_US/propal.lang +++ b/htdocs/langs/en_US/propal.lang @@ -12,9 +12,11 @@ NewPropal=New proposal Prospect=Prospect DeleteProp=Delete commercial proposal ValidateProp=Validate commercial proposal +CancelPropal=Cancel AddProp=Create proposal ConfirmDeleteProp=Are you sure you want to delete this commercial proposal? ConfirmValidateProp=Are you sure you want to validate this commercial proposal under name %s? +ConfirmCancelPropal=Are you sure you want to cancel commercial proposal %s? LastPropals=Latest %s proposals LastModifiedProposals=Latest %s modified proposals AllPropals=All proposals @@ -27,11 +29,13 @@ NbOfProposals=Number of commercial proposals ShowPropal=Show proposal PropalsDraft=Drafts PropalsOpened=Open +PropalStatusCanceled=Canceled (Abandoned) PropalStatusDraft=Draft (needs to be validated) PropalStatusValidated=Validated (proposal is open) PropalStatusSigned=Signed (needs billing) PropalStatusNotSigned=Not signed (closed) PropalStatusBilled=Billed +PropalStatusCanceledShort=Canceled PropalStatusDraftShort=Draft PropalStatusValidatedShort=Validated (open) PropalStatusClosedShort=Closed diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 5a8f96fbde4..e999747f1e1 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -3846,42 +3846,14 @@ class Societe extends CommonObject return 1; } - // Check SIREN if country FR + // Check SIREN if ($idprof == 1 && $soc->country_code == 'FR' && !isValidSiren($this->idprof1)) { return -1; } - // Verifie SIRET si pays FR - if ($idprof == 2 && $soc->country_code == 'FR') { - $chaine = trim($this->idprof2); - $chaine = preg_replace('/(\s)/', '', $chaine); - - if (!is_numeric($chaine)) { - return -1; - } - if (dol_strlen($chaine) != 14) { - return -1; - } - - // on prend chaque chiffre un par un - // si son index (position dans la chaîne en commence à 0 au premier caractère) est pair - // on double sa valeur et si cette dernière est supérieure à 9, on lui retranche 9 - // on ajoute cette valeur à la somme totale - $sum = 0; - for ($index = 0; $index < 14; $index++) { - $number = (int) $chaine[$index]; - if (($index % 2) == 0) { - if (($number *= 2) > 9) { - $number -= 9; - } - } - $sum += $number; - } - - // le numéro est valide si la somme des chiffres est multiple de 10 - if ($sum % 10 != 0) { - return -1; - } + // Check SIRET + if ($idprof == 2 && $soc->country_code == 'FR' && !isValidSiret($this->idprof2)) { + return -1; } //Verify CIF/NIF/NIE if pays ES