diff --git a/ChangeLog b/ChangeLog index 5eb9570711b..e6539a1f1da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,7 +11,8 @@ For users: - New: Add graph report on number of entities in product statistics page. - New: Can delete a supplier order whatever is its status. - New: No limit on free text on PDF generated documents. -- New: can force login value when creating a user from a member. +- New: Can force login value when creating a user from a member. +- New: Can clone a commercial proposal. - Fix: Failed to go on the future view of bank transaction if there is no future bank transaction already wrote. - Fix: Bad ref in supplier list. diff --git a/htdocs/comm/propal.php b/htdocs/comm/propal.php index 60ae2c7ec5f..8b655eba682 100644 --- a/htdocs/comm/propal.php +++ b/htdocs/comm/propal.php @@ -77,6 +77,31 @@ $NBLINES=4; /* Actions */ /******************************************************************************/ +// Action clone object +if ($_POST["action"] == 'confirm_clone' && $_POST['confirm'] == 'yes') +{ + if (1==0 && empty($_REQUEST["clone_content"]) && empty($_REQUEST["clone_receivers"])) + { + $mesg='
'.$langs->trans("NoCloneOptionsSpecified").'
'; + } + else + { + $object=new Propal($db); + $result=$object->createFromClone($_REQUEST['propalid']); + if ($result > 0) + { + header("Location: ".$_SERVER['PHP_SELF'].'?propalid='.$result); + exit; + } + else + { + $mesg=$object->error; + $_GET['action']=''; + $_GET['propalid']=$_REQUEST['propalid']; + } + } +} + // Suppression de la propale if ($_REQUEST['action'] == 'confirm_delete' && $_REQUEST['confirm'] == 'yes') { @@ -850,19 +875,20 @@ llxHeader('',$langs->trans('Proposal'),'Proposition'); $html = new Form($db); $formfile = new FormFile($db); -if ($_GET['propalid'] > 0) +$now=gmmktime(); + +$id = $_GET['propalid']; +$ref= $_GET['ref']; +if ($id > 0 || ! empty($ref)) { /* - * Affichage fiche propal en mode visu - * + * Show object in view mode */ - $now=gmmktime(); - if ($mesg) print $mesg."
"; $propal = new Propal($db); - $propal->fetch($_GET['propalid']); + $propal->fetch($_GET['propalid'],$_GET['ref']); $societe = new Societe($db); $societe->fetch($propal->socid); @@ -870,6 +896,19 @@ if ($_GET['propalid'] > 0) $head = propal_prepare_head($propal); dolibarr_fiche_head($head, 'comm', $langs->trans('Proposal')); + // Clone confirmation + if ($_GET["action"] == 'clone') + { + // Create an array for form + $formquestion=array( + //'text' => $langs->trans("ConfirmClone"), + //array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1) + ); + // Paiement incomplet. On demande si motif = escompte ou autre + $html->form_confirm($_SERVER["PHP_SELF"].'?propalid='.$propal->id,$langs->trans('ClonePropal'),$langs->trans('ConfirmClonePropal',$propal->ref),'confirm_clone',$formquestion,'yes'); + print '
'; + } + /* * Confirmation de la suppression de la propale */ @@ -906,7 +945,9 @@ if ($_GET['propalid'] > 0) print ''; // Ref - print ''; + print ''; // Ref client print ''; print ''; @@ -1727,6 +1768,12 @@ if ($_GET['propalid'] > 0) print '>'.$langs->trans('Close').''; } + // Clone + if ($propal->type == 0 && $user->rights->propale->creer) + { + print ''.$langs->trans("ToClone").''; + } + // Delete if ($propal->statut == 0 && $user->rights->propale->supprimer) { @@ -1874,7 +1921,7 @@ else * Mode Liste des propales * * * ****************************************************************************/ - + $now=gmmktime(); $sortorder=$_GET['sortorder']; diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 6571ad504e1..f1a298f8e02 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -536,7 +536,7 @@ if ($_POST['action'] == 'add' && $user->rights->facture->creer) $error=1; $mesg='
'.$langs->trans("ErrorFieldRequired",$langs->trans("CorrectInvoice")).'
'; } - + $datefacture = dolibarr_mktime(12, 0 , 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); if (empty($datefacture)) { @@ -1650,7 +1650,7 @@ if ($_GET['action'] == 'create') //print ''."\n"; //print ''."\n"; print ''; - + print ''; print ''; print '"; @@ -1666,7 +1666,7 @@ if ($_GET['action'] == 'create') //print ''."\n"; //print ''."\n"; print ''; - + print ''; print ''; print '"; @@ -1880,17 +1880,16 @@ if ($_GET['action'] == 'create') } else { + /* + * Show object in view mode + */ + $now=gmmktime(); - + $id = $_GET['facid']; $ref= $_GET['ref']; if ($id > 0 || ! empty($ref)) { - /* *************************************************************************** */ - /* */ - /* Fiche en mode visu / edition */ - /* */ - /* *************************************************************************** */ if ($mesg) print $mesg.'
'; $facstatic = new Facture($db); @@ -1914,13 +1913,13 @@ else //$resteapayer=bcadd($fac->total_ttc,$totalpaye,$conf->global->MAIN_MAX_DECIMALS_TOT); //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT); $resteapayer = price2num($fac->total_ttc - $totalpaye - $totalavoir,'MT'); - + if ($fac->paye) $resteapayer=0; $resteapayeraffiche=$resteapayer; $absolute_discount=$soc->getAvailableDiscounts('','fk_facture_source IS NULL'); $absolute_creditnote=$soc->getAvailableDiscounts('','fk_facture_source IS NOT NULL'); - + $author = new User($db); if ($fac->user_author) { @@ -2076,8 +2075,8 @@ else $html->form_confirm($_SERVER["PHP_SELF"].'?facid='.$fac->id,$langs->trans('CloneInvoice'),$langs->trans('ConfirmCloneInvoice',$fac->ref),'confirm_clone',$formquestion,'yes'); print '
'; } - - + + // Invoice content print '
'.$langs->trans('Ref').''.$propal->ref.'
'.$langs->trans('Ref').''; + print $html->showrefnav($propal,'ref','',1,'ref','ref',''); + print '
'; @@ -1038,7 +1079,7 @@ if ($_GET['propalid'] > 0) } else { - print dolibarr_print_date($propal->date_livraison,'%a %d %B %Y'); + print dolibarr_print_date($propal->date_livraison,'daytext'); } print '
'.$langs->trans('Proposal').''.img_object($langs->trans("ShowPropal"),'propal').' '.$propal->ref.'
'.$langs->trans('TotalHT').''.price($propal->price).'
'.$langs->trans('TotalVAT').''.price($propal->total_tva)."
'.$langs->trans('Order').''.img_object($langs->trans("ShowOrder"),'order').' '.$commande->ref.'
'.$langs->trans('TotalHT').''.price($commande->total_ht).'
'.$langs->trans('TotalVAT').''.price($commande->total_tva)."
'; @@ -2235,8 +2234,8 @@ else print ' '; } print ''; - - + + // Payments $nbrows=8; if ($conf->global->FAC_USE_CUSTOMER_ORDER_REF) $nbrows++; @@ -2267,7 +2266,7 @@ else print ''; print ''; print ''; - + if ($fac->type != 2) { $var=True; @@ -2293,7 +2292,7 @@ else $resteapayeraffiche=$resteapayer; $creditnoteamount=0; - + // Loop on each credit note applied $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,"; $sql.= " re.description, re.fk_facture_source, re.fk_facture_source"; @@ -3071,7 +3070,7 @@ else { print ''.$langs->trans("ToClone").''; } - + // Clone as predefined if ($conf->global->FACTURE_ENABLE_RECUR && $fac->type == 0 && $fac->statut == 0 && $user->rights->facture->creer) { @@ -3081,7 +3080,7 @@ else } } - // Supprimer + // Delete if ($fac->is_erasable() && $user->rights->facture->supprimer && $_GET['action'] != 'delete') { if ($facidnext) diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 29a2078a577..e2e8e1a7ac2 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -242,7 +242,7 @@ RemoveDiscount=Remove discount WatermarkOnDraftBill=Watermark on draft invoices (nothing if empty) CloneInvoice=Clone invoice CloneMainAttributes=Clone object with its main attributes -ConfirmCloneInvoice=Are you sure you want to clone this invoice ? +ConfirmCloneInvoice=Are you sure you want to clone this invoice %s ? DisabledBecauseReplacedInvoice=Action disabled because invoice has been replaced # PaymentConditions diff --git a/htdocs/langs/en_US/propal.lang b/htdocs/langs/en_US/propal.lang index ed0d1eb775e..011e043baa5 100644 --- a/htdocs/langs/en_US/propal.lang +++ b/htdocs/langs/en_US/propal.lang @@ -73,6 +73,8 @@ CopyPropalFrom=Create commercial proposal by copying existing proposal CreateEmptyPropal=Create empty commercial proposals vierge or from list of products/services DefaultProposalDurationValidity=Default commercial proposal validity duration (in days) UseCustomerContactAsPropalRecipientIfExist=Use customer contact address if defined instead of third party address as proposal recipient address +ClonePropal=Clone commercial proposal +ConfirmClonePropal=Are you sure you want to clone this commercial proposal %s ? # Document models DocModelAzurDescription=A complete proposal model (logo...) diff --git a/htdocs/langs/fr_FR/bills.lang b/htdocs/langs/fr_FR/bills.lang index be763c0823f..fe2da25434b 100644 --- a/htdocs/langs/fr_FR/bills.lang +++ b/htdocs/langs/fr_FR/bills.lang @@ -242,7 +242,7 @@ WatermarkOnDraftBill=Filigrane sur les brouillons de factures (aucun si vide) UnpayedNotChecked=Aucune facture impayées n'a été sélectionnée CloneInvoice=Cloner facture CloneMainAttributes=Cloner l'objet avec ces attributs principaux -ConfirmCloneInvoice=Etes-vous sur de vouloir cloner cette facture ? +ConfirmCloneInvoice=Etes-vous sur de vouloir cloner cette facture %s ? DisabledBecauseReplacedInvoice=Action désactivée car facture remplacée # PaymentConditions diff --git a/htdocs/langs/fr_FR/propal.lang b/htdocs/langs/fr_FR/propal.lang index aeb947c7800..a707bd27c57 100644 --- a/htdocs/langs/fr_FR/propal.lang +++ b/htdocs/langs/fr_FR/propal.lang @@ -73,6 +73,8 @@ CopyPropalFrom=Cr CreateEmptyPropal=Créer proposition/devis vierge ou depuis liste de produits prédéfinis DefaultProposalDurationValidity=Délai de validité par défaut (en jours) UseCustomerContactAsPropalRecipientIfExist=Utiliser adresse contact suivi client si défini plutot que adresse tiers comme destinataire des propositions +ClonePropal=Cloner proposition commerciale +ConfirmClonePropal=Etes-vous sur de vouloir cloner cette proposition commerciale %s ? # Documents models DocModelAzurDescription=Modèle de propositions commerciales complet (logo...) diff --git a/htdocs/propal.class.php b/htdocs/propal.class.php index 16279733644..ff16145af37 100644 --- a/htdocs/propal.class.php +++ b/htdocs/propal.class.php @@ -269,7 +269,7 @@ class Propal extends CommonObject dolibarr_syslog("Propal::Addline propalid=$propalid, 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"); include_once(DOL_DOCUMENT_ROOT.'/lib/price.lib.php'); - + if ($this->statut == 0) { $this->db->begin(); @@ -306,7 +306,7 @@ class Propal extends CommonObject $total_ht = $tabprice[0]; $total_tva = $tabprice[1]; $total_ttc = $tabprice[2]; - + // \TODO A virer // Anciens indicateurs: $price, $remise (a ne plus utiliser) $price = $pu; @@ -333,7 +333,7 @@ class Propal extends CommonObject $ligne->total_ht=$total_ht; $ligne->total_tva=$total_tva; $ligne->total_ttc=$total_ttc; - + // Mise en option de la ligne if ($conf->global->PROPALE_USE_OPTION_LINE && !$qty) $ligne->special_code=3; @@ -384,14 +384,14 @@ class Propal extends CommonObject function updateline($rowid, $pu, $qty, $remise_percent=0, $txtva, $desc='', $price_base_type='HT', $info_bits=0) { global $conf; - + dolibarr_syslog("Propal::UpdateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits"); include_once(DOL_DOCUMENT_ROOT.'/lib/price.lib.php'); - + if ($this->statut == 0) { $this->db->begin(); - + // Nettoyage param�tres $remise_percent=price2num($remise_percent); $qty=price2num($qty); @@ -505,7 +505,7 @@ class Propal extends CommonObject function create($user='') { global $langs,$conf,$mysoc; - + // on verifie si la ref n'est pas utilisee $soc = new Societe($this->db); $soc->fetch($this->socid); @@ -514,8 +514,6 @@ class Propal extends CommonObject // Clean parameters $this->fin_validite = $this->datep + ($this->duree_validite * 24 * 3600); - dolibarr_syslog("Propal::create ref=".$this->ref); - $this->db->begin(); $this->fetch_client(); @@ -539,6 +537,7 @@ class Propal extends CommonObject if ($conf->global->PROPALE_ADD_SHIPPING_DATE) $sql.= ", ".($this->date_livraison?$this->db->idate($this->date_livraison):'null'); $sql.= ")"; + dolibarr_syslog("Propal::create sql=".$sql, LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) { @@ -632,52 +631,101 @@ class Propal extends CommonObject */ function create_from($user) { - global $conf,$lang; - - $this->products=array(); - $i=0; - foreach($this->lignes as $ligne) - { - $this->products[$i]->desc=$ligne->desc; - $this->products[$i]->subprice=$ligne->subprice; - $this->products[$i]->qty=$ligne->qty; - $this->products[$i]->tva_tx=$ligne->tva_tx; - $this->products[$i]->fk_product=$ligne->fk_product; - $this->products[$i]->remise_percent=$ligne->remise_percent; - - $i++; - } + $this->products=$this->lignes; return $this->create(); } /** - * \brief Recupere de la base les caracteristiques d'une propale - * \param rowid id de la propal a recuperer - * \return int <0 si ko, 0 si non trouve, >0 si ok + * \brief Load an object from its id and create a new one in database + * \param fromid Id of object to clone + * \param invertdetail Reverse sign of amounts for lines + * \return int New id of clone */ - function fetch($rowid) + function createFromClone($fromid,$invertdetail=0) + { + global $user,$langs; + + $error=0; + + $object=new Propal($this->db); + + $this->db->begin(); + + // Load source object + $object->fetch($fromid); + $object->id=0; + $object->statut=0; + + // Clear fields + $object->user_author = $user->id; + $object->user_valid = ''; + $object->date = ''; + $object->datep = gmmktime(); + $object->fin_validite = ''; + $object->ref_client = ''; + $object->products = $object->lignes; // Tant que products encore utilisé + + // Create clone + $result=$object->create($user); + + // Other options + if ($result < 0) + { + $this->error=$object->error; + $error++; + } + + if (! $error) + { + + + + } + + // End + if (! $error) + { + $this->db->commit(); + return $object->id; + } + else + { + $this->db->rollback(); + return -1; + } + } + + /** + * \brief Load a proposal from database and its ligne array + * \param rowid id of object to load + * \param ref Ref of proposal + * \return int >0 if OK, <0 if KO + */ + function fetch($rowid,$ref='') { $sql = "SELECT p.rowid,ref,remise,remise_percent,remise_absolue,fk_soc"; $sql.= ", total, tva, total_ht"; - $sql.= ", ".$this->db->pdate("datep")." as dp"; - $sql.= ", ".$this->db->pdate("fin_validite")." as dfv"; - $sql.= ", ".$this->db->pdate("date_livraison")." as date_livraison"; + $sql.= ", datep as dp"; + $sql.= ", fin_validite as dfv"; + $sql.= ", date_livraison as date_livraison"; $sql.= ", model_pdf, ref_client"; $sql.= ", note, note_public"; $sql.= ", fk_projet, fk_statut, fk_user_author"; $sql.= ", fk_adresse_livraison"; - $sql.= ", p.fk_cond_reglement, cr.code as cond_reglement_code"; - $sql.= ", p.fk_mode_reglement, cp.code as mode_reglement_code"; + $sql.= ", p.fk_cond_reglement"; + $sql.= ", p.fk_mode_reglement"; $sql.= ", c.label as statut_label"; + $sql.= ", cr.code as cond_reglement_code, cr.libelle, cr.libelle_facture"; + $sql.= ", cp.code as mode_reglement_code"; $sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."propal as p"; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'cond_reglement as cr ON p.fk_cond_reglement = cr.rowid'; $sql.= " WHERE p.fk_statut = c.id"; - $sql.= " AND p.rowid='".$rowid."'"; - - dolibarr_syslog("Propal::fecth rowid=".$rowid); + if ($ref) $sql.= " AND p.ref='".$ref."'"; + else $sql.= " AND p.rowid=".$rowid; + dolibarr_syslog("Propal::fecth sql=".$sql, LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) { @@ -685,11 +733,8 @@ class Propal extends CommonObject { $obj = $this->db->fetch_object($resql); - $this->id = $rowid; + $this->id = $obj->rowid; - $this->date = $obj->dp; - $this->datep = $obj->dp; - $this->fin_validite = $obj->dfv; $this->ref = $obj->ref; $this->ref_client = $obj->ref_client; $this->remise = $obj->remise; @@ -706,32 +751,22 @@ class Propal extends CommonObject $this->note_public = $obj->note_public; $this->statut = $obj->fk_statut; $this->statut_libelle = $obj->statut_label; + + $this->date = $this->db->jdate($obj->dp); + $this->datep = $this->db->jdate($obj->dp); + $this->fin_validite = $this->db->jdate($obj->dfv); + $this->date_livraison = $this->db->jdate($obj->date_livraison); + $this->adresse_livraison_id = $obj->fk_adresse_livraison; + $this->mode_reglement_id = $obj->fk_mode_reglement; $this->mode_reglement_code = $obj->mode_reglement_code; $this->cond_reglement_id = $obj->fk_cond_reglement; $this->cond_reglement_code = $obj->cond_reglement_code; - $this->date_livraison = $obj->date_livraison; - $this->adresse_livraison_id = $obj->fk_adresse_livraison; + $this->cond_reglement = $objc->libelle; + $this->cond_reglement_document = $objc->libelle_facture; $this->user_author_id = $obj->fk_user_author; - if ($this->cond_reglement_id) - { - $sql = "SELECT rowid, libelle, code, libelle_facture"; - $sql.= " FROM ".MAIN_DB_PREFIX."cond_reglement"; - $sql.= " WHERE rowid = ".$this->cond_reglement_id; - - $resqlcond = $this->db->query($sql); - - if ($resqlcond) - { - $objc = $this->db->fetch_object($resqlcond); - $this->cond_reglement = $objc->libelle; - $this->cond_reglement_document = $objc->libelle_facture; - $this->cond_reglement_code = $objc->code; - } - } - if ($obj->fk_statut == 0) { $this->brouillon = 1; @@ -741,7 +776,7 @@ class Propal extends CommonObject $this->db->free($resql); /* - * Lignes propales li�es � un produit ou non + * Lignes propales liees a un produit ou non */ $sql = "SELECT d.description, d.price, d.tva_tx, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,"; $sql.= " d.info_bits, d.total_ht, d.total_tva, d.total_ttc, d.marge_tx, d.marque_tx, d.special_code, d.rang,"; @@ -796,7 +831,7 @@ class Propal extends CommonObject else { $this->error=$this->db->error(); - dolibarr_syslog("Propal::Fetch Error $this->error, sql=$sql"); + dolibarr_syslog("Propal::Fetch Error ".$this->error, LOG_ERROR); return -1; } @@ -809,7 +844,7 @@ class Propal extends CommonObject else { $this->error=$this->db->error(); - dolibarr_syslog("Propal::Fetch Error sql=$sql ".$this->error); + dolibarr_syslog("Propal::Fetch Error ".$this->error, LOG_ERROR); return -1; } } @@ -1369,7 +1404,7 @@ class Propal extends CommonObject } $this->db->free($resql); $nump = $this->db->num_rows($resql2); - + for ($i = 0;$i < $nump;$i++) { $sqlobj = $this->db->fetch_object($resql2); @@ -1378,10 +1413,10 @@ class Propal extends CommonObject } $this->db->free($resql2); //array_multisort ($tab_sqlobjOrder,$tab_sqlobj); - + //$nump = $this->db->num_rows($resql); $nump = sizeOf($tab_sqlobj); - + if ($nump) { $i = 0; @@ -1389,7 +1424,7 @@ class Propal extends CommonObject { //$obj = $this->db->fetch_object($resql); $obj = array_shift($tab_sqlobj); - + $ga[$obj->rowid] = $obj->facnumber; $i++; } @@ -1411,7 +1446,7 @@ class Propal extends CommonObject global $conf; $this->db->begin(); - + $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id; if ( $this->db->query($sql) ) { @@ -1664,7 +1699,7 @@ class Propal extends CommonObject global $conf, $user; $now=gmmktime(); - + $this->nbtodo=$this->nbtodolate=0; $clause = "WHERE"; @@ -1684,11 +1719,11 @@ class Propal extends CommonObject { if ($mode == 'opened') $delay_warning=$conf->propal->cloture->warning_delay; if ($mode == 'signed') $delay_warning=$conf->propal->facture->warning_delay; - + while ($obj=$this->db->fetch_object($resql)) { $this->nbtodo++; - if ($mode == 'opened') + if ($mode == 'opened') { $datelimit = $this->db->jdate($obj->datefin); if ($datelimit < ($now - $delay_warning)) @@ -1839,7 +1874,7 @@ class Propal extends CommonObject } } } - + /** * \brief Renvoie la r�f�rence de propale suivante non utilis�e en fonction du module
'.$langs->trans('Amount').'