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='
';
// Ref
- print ''.$langs->trans('Ref').' '.$propal->ref.' ';
+ print ''.$langs->trans('Ref').' ';
+ print $html->showrefnav($propal,'ref','',1,'ref','ref','');
+ print ' ';
// Ref client
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 ' ';
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 ''.$langs->trans('Proposal').' '.img_object($langs->trans("ShowPropal"),'propal').' '.$propal->ref.' ';
print ''.$langs->trans('TotalHT').' '.price($propal->price).' ';
print ''.$langs->trans('TotalVAT').' '.price($propal->total_tva)." ";
@@ -1666,7 +1666,7 @@ if ($_GET['action'] == 'create')
//print ' '."\n";
//print ' '."\n";
print ' ';
-
+
print ''.$langs->trans('Order').' '.img_object($langs->trans("ShowOrder"),'order').' '.$commande->ref.' ';
print ''.$langs->trans('TotalHT').' '.price($commande->total_ht).' ';
print ''.$langs->trans('TotalVAT').' '.price($commande->total_tva)." ";
@@ -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 '';
@@ -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 ''.$langs->trans('Amount').' ';
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