Can clone a commercial proposal

This commit is contained in:
Laurent Destailleur 2009-01-14 15:53:52 +00:00
parent 6e15ff8cf3
commit 192cb68c28
8 changed files with 188 additions and 102 deletions

View File

@ -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.

View File

@ -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='<div class="error">'.$langs->trans("NoCloneOptionsSpecified").'</div>';
}
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."<br>";
$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 '<br>';
}
/*
* Confirmation de la suppression de la propale
*/
@ -906,7 +945,9 @@ if ($_GET['propalid'] > 0)
print '<table class="border" width="100%">';
// Ref
print '<tr><td>'.$langs->trans('Ref').'</td><td colspan="5">'.$propal->ref.'</td></tr>';
print '<tr><td>'.$langs->trans('Ref').'</td><td colspan="5">';
print $html->showrefnav($propal,'ref','',1,'ref','ref','');
print '</td></tr>';
// Ref client
print '<tr><td>';
@ -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 '</td>';
print '</tr>';
@ -1727,6 +1768,12 @@ if ($_GET['propalid'] > 0)
print '>'.$langs->trans('Close').'</a>';
}
// Clone
if ($propal->type == 0 && $user->rights->propale->creer)
{
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?propalid='.$propal->id.'&amp;action=clone&amp;object=propal">'.$langs->trans("ToClone").'</a>';
}
// Delete
if ($propal->statut == 0 && $user->rights->propale->supprimer)
{
@ -1874,7 +1921,7 @@ else
* Mode Liste des propales *
* *
****************************************************************************/
$now=gmmktime();
$sortorder=$_GET['sortorder'];

View File

@ -536,7 +536,7 @@ if ($_POST['action'] == 'add' && $user->rights->facture->creer)
$error=1;
$mesg='<div class="error">'.$langs->trans("ErrorFieldRequired",$langs->trans("CorrectInvoice")).'</div>';
}
$datefacture = dolibarr_mktime(12, 0 , 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']);
if (empty($datefacture))
{
@ -1650,7 +1650,7 @@ if ($_GET['action'] == 'create')
//print '<input type="hidden" name="remise_absolue" value="'.$propal->remise_absolue.'">'."\n";
//print '<input type="hidden" name="remise_percent" value="'.$propal->remise_percent.'">'."\n";
print '<input type="hidden" name="propalid" value="'.$propal->id.'">';
print '<tr><td>'.$langs->trans('Proposal').'</td><td colspan="2"><a href="'.DOL_URL_ROOT.'/comm/propal.php?propalid='.$propal->id.'">'.img_object($langs->trans("ShowPropal"),'propal').' '.$propal->ref.'</a></td></tr>';
print '<tr><td>'.$langs->trans('TotalHT').'</td><td colspan="2">'.price($propal->price).'</td></tr>';
print '<tr><td>'.$langs->trans('TotalVAT').'</td><td colspan="2">'.price($propal->total_tva)."</td></tr>";
@ -1666,7 +1666,7 @@ if ($_GET['action'] == 'create')
//print '<input type="hidden" name="remise_absolue" value="'.$commande->remise_absolue.'">'."\n";
//print '<input type="hidden" name="remise_percent" value="'.$commande->remise_percent.'">'."\n";
print '<input type="hidden" name="commandeid" value="'.$commande->id.'">';
print '<tr><td>'.$langs->trans('Order').'</td><td colspan="2"><a href="'.DOL_URL_ROOT.'/commande/fiche.php?id='.$commande->id.'">'.img_object($langs->trans("ShowOrder"),'order').' '.$commande->ref.'</a></td></tr>';
print '<tr><td>'.$langs->trans('TotalHT').'</td><td colspan="2">'.price($commande->total_ht).'</td></tr>';
print '<tr><td>'.$langs->trans('TotalVAT').'</td><td colspan="2">'.price($commande->total_tva)."</td></tr>";
@ -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.'<br>';
$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 '<br>';
}
// Invoice content
print '<table class="border" width="100%">';
@ -2235,8 +2234,8 @@ else
print '&nbsp;';
}
print '</td>';
// Payments
$nbrows=8;
if ($conf->global->FAC_USE_CUSTOMER_ORDER_REF) $nbrows++;
@ -2267,7 +2266,7 @@ else
print '<td align="right">'.$langs->trans('Amount').'</td>';
print '<td width="18">&nbsp;</td>';
print '</tr>';
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 '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?facid='.$fac->id.'&amp;action=clone&amp;object=invoice">'.$langs->trans("ToClone").'</a>';
}
// 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)

View File

@ -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 <b>%s</b> ?
DisabledBecauseReplacedInvoice=Action disabled because invoice has been replaced
# PaymentConditions

View File

@ -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 <b>%s</b> ?
# Document models
DocModelAzurDescription=A complete proposal model (logo...)

View File

@ -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 <b>%s</b> ?
DisabledBecauseReplacedInvoice=Action désactivée car facture remplacée
# PaymentConditions

View File

@ -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 <b>%s</b> ?
# Documents models
DocModelAzurDescription=Modèle de propositions commerciales complet (logo...)

View File

@ -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<61>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<EFBFBD>es <EFBFBD> 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<EFBFBD>f<EFBFBD>rence de propale suivante non utilis<EFBFBD>e en fonction du module