diff --git a/htdocs/admin/propale.php b/htdocs/admin/propale.php index f9e53d906d1..6eb6ec321ef 100644 --- a/htdocs/admin/propale.php +++ b/htdocs/admin/propale.php @@ -100,6 +100,13 @@ if ($_POST["action"] == 'setadddeliveryaddress') exit; } +if ($_POST["action"] == 'setuseoptionline') +{ + dolibarr_set_const($db, "PROPALE_USE_OPTION_LINE",$_POST["value"]); + Header("Location: propale.php"); + exit; +} + if ($_POST["action"] == 'setclassifiedinvoiced') { dolibarr_set_const($db, "PROPALE_CLASSIFIED_INVOICED_WITH_ORDER",$_POST["value"]); @@ -107,9 +114,9 @@ if ($_POST["action"] == 'setclassifiedinvoiced') exit; } -if ($_POST["action"] == 'set_use_customer_contact_as_recipient') +if ($_POST["action"] == 'setusecustomercontactasrecipient') { - dolibarr_set_const($db, "PROPALE_USE_CUSTOMER_CONTACT_AS_RECIPIENT",$_POST["use_customer_contact_as_recipient"]); + dolibarr_set_const($db, "PROPALE_USE_CUSTOMER_CONTACT_AS_RECIPIENT",$_POST["value"]); Header("Location: propale.php"); exit; } @@ -429,11 +436,23 @@ print ''; $var=! $var; print '
'; -print ''; +print ''; print ''; print $langs->trans("UseCustomerContactAsPropalRecipientIfExist"); print ''; -print $html->selectyesno("use_customer_contact_as_recipient",$conf->global->PROPALE_USE_CUSTOMER_CONTACT_AS_RECIPIENT,1); +print $html->selectyesno("value",$conf->global->PROPALE_USE_CUSTOMER_CONTACT_AS_RECIPIENT,1); +print ''; +print ''; +print "\n"; +print '
'; + +$var=! $var; +print '
'; +print ''; +print ''; +print $langs->trans("UseOptionLineIfNoQuantity"); +print ''; +print $html->selectyesno("value",$conf->global->PROPALE_USE_OPTION_LINE,1); print ''; print ''; print "\n"; diff --git a/htdocs/comm/propal.php b/htdocs/comm/propal.php index 2355efaec98..5d366f94445 100644 --- a/htdocs/comm/propal.php +++ b/htdocs/comm/propal.php @@ -506,7 +506,7 @@ if ($_POST['action'] == "setabsolutediscount" && $user->rights->propale->creer) */ if ($_POST['action'] == "addligne" && $user->rights->propale->creer) { - if ($_POST['qty'] && (($_POST['np_price']!='' && ($_POST['np_desc'] || $_POST['dp_desc'])) || $_POST['idprod'])) + if (isset($_POST['qty']) && (($_POST['np_price']!='' && ($_POST['np_desc'] || $_POST['dp_desc'])) || $_POST['idprod'])) { $propal = new Propal($db); $ret=$propal->fetch($_POST['propalid']); @@ -1068,7 +1068,7 @@ if ($_GET['propalid'] > 0) $sql = 'SELECT pt.rowid, pt.description, pt.fk_product, pt.fk_remise_except,'; $sql.= ' pt.qty, pt.tva_tx, pt.remise_percent, pt.subprice, pt.info_bits,'; - $sql.= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.marge_tx, pt.marque_tx,pt.pa_ht,'; + $sql.= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.marge_tx, pt.marque_tx, pt.pa_ht, pt.special_code,'; $sql.= ' p.label as product, p.ref, p.fk_product_type, p.rowid as prodid,'; $sql.= ' p.description as product_desc'; $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pt'; @@ -1112,8 +1112,7 @@ if ($_GET['propalid'] > 0) if ($objp->fk_product > 0) { print ''; - print ''; // ancre pour retourner sur la ligne - $text = ''; + print ''; // ancre pour retourner sur la ligne; // Affiche ligne produit $text = ''; @@ -1223,7 +1222,7 @@ if ($_GET['propalid'] > 0) // Qty print ''; - if (($objp->info_bits & 2) != 2) + if ((($objp->info_bits & 2) != 2) && $objp->special_code != 3) { print $objp->qty; } @@ -1231,7 +1230,7 @@ if ($_GET['propalid'] > 0) print ''; // Remise % - if ($objp->remise_percent > 0) + if ($objp->remise_percent > 0 && $objp->special_code != 3) { print ''.dolibarr_print_reduction($objp->remise_percent)."\n"; } @@ -1239,7 +1238,17 @@ if ($_GET['propalid'] > 0) { print ' '; } - print ''.price($objp->total_ht)."\n"; + + // Montant total HT + if ($objp->special_code == 3) + { + // Si ligne en option + print ''.$langs->trans('Option').''; + } + else + { + print ''.price($objp->total_ht)."\n"; + } // Icone d'edition et suppression if ($propal->statut == 0 && $user->rights->propale->creer) diff --git a/htdocs/includes/modules/propale/pdf_propale_azur.modules.php b/htdocs/includes/modules/propale/pdf_propale_azur.modules.php index db2ad520208..8e311ac1170 100644 --- a/htdocs/includes/modules/propale/pdf_propale_azur.modules.php +++ b/htdocs/includes/modules/propale/pdf_propale_azur.modules.php @@ -288,21 +288,29 @@ class pdf_propale_azur extends ModelePDFPropales // Quantité $pdf->SetXY ($this->posxqty, $curY); - $pdf->MultiCell(10, 4, $propale->lignes[$i]->qty, 0, 'R'); + if ($propale->lignes[$i]->special_code != 3) $pdf->MultiCell(10, 4, $propale->lignes[$i]->qty, 0, 'R'); // Remise sur ligne $pdf->SetXY ($this->posxdiscount, $curY); - if ($propale->lignes[$i]->remise_percent) + if ($propale->lignes[$i]->remise_percent && $propale->lignes[$i]->special_code != 3) { $pdf->MultiCell(14, 4, dolibarr_print_reduction($propale->lignes[$i]->remise_percent), 0, 'R'); } // Total HT ligne $pdf->SetXY ($this->postotalht, $curY); - $total = price($propale->lignes[$i]->total_ht); - $pdf->MultiCell(23, 4, $total, 0, 'R', 0); + if ($propale->lignes[$i]->special_code == 3) + { + // Ligne produit en option + $pdf->MultiCell(23, 4, $outputlangs->transnoentities("Option"), 0, 'R', 0); + } + else + { + $total = price($propale->lignes[$i]->total_ht); + $pdf->MultiCell(23, 4, $total, 0, 'R', 0); + } - // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva + // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva $tvaligne=$propale->lignes[$i]->total_tva; if ($propale->remise_percent) $tvaligne-=($tvaligne*$propale->remise_percent)/100; $this->tva[(string) $propale->lignes[$i]->tva_tx] += $tvaligne; diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 8d5c0d934ef..e3b15bce3b9 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -547,6 +547,7 @@ ClassifiedInvoicedWithOrder=Classify invoiced proposal at the same time as the o HideTreadedPropal=Hide the treated commercial proposals in the list AddShippingDateAbility=Add shipping date ability AddDeliveryAddressAbility=Add delivery date ability +UseOptionLineIfNoQuantity=A line of product/service with a zero amount is considered as an option ##### Orders ##### OrdersSetup=Orders' management setup OrdersNumberingModules=Orders numbering modules diff --git a/htdocs/langs/fr_FR/admin.lang b/htdocs/langs/fr_FR/admin.lang index 0be48538be6..37b0ab2265e 100644 --- a/htdocs/langs/fr_FR/admin.lang +++ b/htdocs/langs/fr_FR/admin.lang @@ -546,6 +546,7 @@ ClassifiedInvoicedWithOrder=Class HideTreadedPropal=Cacher les propositions commerciales traitées de la liste AddShippingDateAbility=Possibilité de déterminer une date de livraison AddDeliveryAddressAbility=Possibilité de sélectionner une adresse de livraison +UseOptionLineIfNoQuantity=Une ligne de produit/service ayant une quantité nulle est considérée comme une option ##### Orders ##### OrdersSetup=Configuration du module Commandes OrdersNumberingModules=Modules de numérotation des commandes diff --git a/htdocs/propal.class.php b/htdocs/propal.class.php index 1d1f0fc4b03..95c9ff43cda 100644 --- a/htdocs/propal.class.php +++ b/htdocs/propal.class.php @@ -270,6 +270,8 @@ class Propal extends CommonObject */ function addline($propalid, $desc, $pu_ht, $qty, $txtva, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0) { + global $conf; + 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"); include_once(DOL_DOCUMENT_ROOT.'/lib/price.lib.php'); @@ -280,7 +282,14 @@ class Propal extends CommonObject // Nettoyage paramètres $remise_percent=price2num($remise_percent); $qty=price2num($qty); - if (! $qty) $qty=1; + if ($conf->global->PROPALE_USE_OPTION_LINE && !$qty) + { + $qty=0; + } + else if (! $qty) + { + $qty=1; + } $pu_ht=price2num($pu_ht); $pu_ttc=price2num($pu_ttc); $txtva=price2num($txtva); @@ -329,6 +338,9 @@ 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; // \TODO Ne plus utiliser $ligne->price=$price; @@ -336,30 +348,31 @@ class Propal extends CommonObject $result=$ligne->insert(); if ($result > 0) - { - // Mise a jour informations denormalisees au niveau de la facture meme + { + // Mise a jour informations denormalisees au niveau de la propale meme $result=$this->update_price($propalid); - if ($result > 0) - { + + if ($result > 0) + { $this->db->commit(); return 1; - } - else - { - $this->error=$this->db->error(); - dolibarr_syslog("Error sql=$sql, error=".$this->error); + } + else + { + $this->error=$this->db->error(); + dolibarr_syslog("Error sql=$sql, error=".$this->error); $this->db->rollback(); return -1; - } - } - else - { - $this->error=$ligne->error; + } + } + else + { + $this->error=$ligne->error; $this->db->rollback(); - return -2; - } - } + return -2; + } } + } /** @@ -375,71 +388,90 @@ class Propal extends CommonObject */ function updateline($rowid, $pu, $qty, $remise_percent=0, $txtva, $desc='', $price_base_type='HT') { + global $conf; + dolibarr_syslog("Propal::UpdateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type"); - include_once(DOL_DOCUMENT_ROOT.'/lib/price.lib.php'); - - if ($this->statut == 0) + 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); + if ($conf->global->PROPALE_USE_OPTION_LINE && !$qty) + { + $qty=0; + $remise_percent=0; + } + else if (! $qty) + { + $qty=1; + } + $pu = price2num($pu); + $txtva = price2num($txtva); + + // Calcul du total TTC et de la TVA pour la ligne a partir de + // qty, pu, remise_percent et txtva + // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker + // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. + $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, 0, $price_base_type); + $total_ht = $tabprice[0]; + $total_tva = $tabprice[1]; + $total_ttc = $tabprice[2]; + + // Anciens indicateurs: $price, $remise (a ne plus utiliser) + $price = $pu; + if ($remise_percent > 0) { - $this->db->begin(); - - // Nettoyage paramètres - $remise_percent=price2num($remise_percent); - $qty=price2num($qty); - if (! $qty) $qty=1; - $pu = price2num($pu); - $txtva = price2num($txtva); - - // Calcul du total TTC et de la TVA pour la ligne a partir de - // qty, pu, remise_percent et txtva - // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker - // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. - $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, 0, $price_base_type); - $total_ht = $tabprice[0]; - $total_tva = $tabprice[1]; - $total_ttc = $tabprice[2]; - - // Anciens indicateurs: $price, $remise (a ne plus utiliser) - $price = $pu; - if ($remise_percent > 0) - { - $remise = round(($pu * $remise_percent / 100), 2); - $price = $pu - $remise; - } - - $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet "; - $sql.= " SET qty='".$qty."'"; - $sql.= " , price='". price2num($price)."'"; // \TODO A virer - $sql.= " , remise_percent='".$remise_percent."'"; // \TODO A virer - $sql.= " , subprice=".price2num($pu); - $sql.= " , tva_tx=".price2num($txtva); - $sql.= " , description='".addslashes($desc)."'"; - $sql.= " , total_ht=".price2num($total_ht); - $sql.= " , total_tva=".price2num($total_tva); - $sql.= " , total_ttc=".price2num($total_ttc); - $sql.= " WHERE rowid = '".$rowid."';"; - - $result=$this->db->query($sql); - if ($result > 0) - { - $this->update_price(); - $this->db->commit(); - return 0; - } - else - { - $this->error=$this->db->error(); - $this->db->rollback(); - dolibarr_syslog("Propal.class::UpdateLine Erreur sql=$sql, error=".$this->error); - return -1; - } + $remise = round(($pu * $remise_percent / 100), 2); + $price = $pu - $remise; + } + + $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet "; + $sql.= " SET qty='".$qty."'"; + $sql.= " , price='". price2num($price)."'"; // \TODO A virer + $sql.= " , remise_percent='".$remise_percent."'"; // \TODO A virer + $sql.= " , subprice=".price2num($pu); + $sql.= " , tva_tx=".price2num($txtva); + $sql.= " , description='".addslashes($desc)."'"; + $sql.= " , total_ht=".price2num($total_ht); + $sql.= " , total_tva=".price2num($total_tva); + $sql.= " , total_ttc=".price2num($total_ttc); + if ($conf->global->PROPALE_USE_OPTION_LINE && !$qty) + { + $sql.= " , special_code=3"; } else { - dolibarr_syslog("Propal.class::UpdateLigne Erreur -2 Propal en mode incompatible pour cette action"); - return -2; + $sql.= " , special_code=0"; } + $sql.= " WHERE rowid = '".$rowid."';"; + + $result=$this->db->query($sql); + if ($result > 0) + { + $this->update_price(); + $this->db->commit(); + return 0; + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + dolibarr_syslog("Propal.class::UpdateLine Erreur sql=$sql, error=".$this->error); + return -1; + } + } + else + { + dolibarr_syslog("Propal.class::UpdateLigne Erreur -2 Propal en mode incompatible pour cette action"); + return -2; + } } + /** * \brief Supprime une ligne de detail * \param idligne Id de la ligne detail à supprimer @@ -604,7 +636,7 @@ class Propal extends CommonObject // Liste des lignes a sommer $sql = "SELECT qty, tva_tx, subprice, remise_percent,"; - $sql.= " total_ht, total_tva, total_ttc"; + $sql.= " total_ht, total_tva, total_ttc, special_code"; $sql.= " FROM ".MAIN_DB_PREFIX."propaldet"; $sql.= " WHERE fk_propal = ".$this->id; @@ -612,46 +644,50 @@ class Propal extends CommonObject $result = $this->db->query($sql); if ($result) { - $this->total_ht = 0; - $this->total_tva = 0; - $this->total_ttc = 0; + $this->total_ht = 0; + $this->total_tva = 0; + $this->total_ttc = 0; - $num = $this->db->num_rows($result); - $i = 0; - while ($i < $num) - { - $obj = $this->db->fetch_object($result); - - $this->total_ht += $obj->total_ht; - $this->total_tva += ($obj->total_ttc - $obj->total_ht); - $this->total_ttc += $obj->total_ttc; - - $tvas[$obj->tva_taux] += ($obj->total_ttc - $obj->total_ht); - $i++; - } - - $this->db->free($result); + $num = $this->db->num_rows($result); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($result); + + if ($this->special_code != 3) + { + $this->total_ht += $obj->total_ht; + $this->total_tva += ($obj->total_ttc - $obj->total_ht); + $this->total_ttc += $obj->total_ttc; + + $tvas[$obj->tva_taux] += ($obj->total_ttc - $obj->total_ht); + } + + $i++; + } + + $this->db->free($result); // Met a jour indicateurs - $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET"; - $sql.= " total_ht=".price2num($this->total_ht).","; - $sql.= " tva=". price2num($this->total_tva).","; - $sql.= " total=". price2num($this->total_ttc); - $sql.= " WHERE rowid = ".$this->id; + $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET"; + $sql.= " total_ht=".price2num($this->total_ht).","; + $sql.= " tva=". price2num($this->total_tva).","; + $sql.= " total=". price2num($this->total_ttc); + $sql.= " WHERE rowid = ".$this->id; dolibarr_syslog("Propal::update_price sql=".$sql); - if ( $this->db->query($sql) ) - { - return 1; - } - else - { - $this->error=$this->db->error(); - dolibarr_syslog("Propal::update_price error=".$this->error); - return -1; - } - } - else + if ( $this->db->query($sql) ) + { + return 1; + } + else + { + $this->error=$this->db->error(); + dolibarr_syslog("Propal::update_price error=".$this->error); + return -1; + } + } + else { $this->error=$this->db->error(); dolibarr_syslog("Propal::update_price error=".$this->error,LOG_ERR); @@ -876,7 +912,7 @@ class Propal extends CommonObject * Lignes propales liées à 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.rang,"; + $sql.= " d.info_bits, d.total_ht, d.total_tva, d.total_ttc, d.marge_tx, d.marque_tx, d.special_code, d.rang,"; $sql.= " p.ref, p.label, p.description as product_desc"; $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as d"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid"; @@ -909,6 +945,7 @@ class Propal extends CommonObject $ligne->total_ttc = $objp->total_ttc; $ligne->marge_tx = $objp->marge_tx; $ligne->marque_tx = $objp->marque_tx; + $ligne->special_code = $objp->special_code; $ligne->rang = $objp->rang; $ligne->fk_product = $objp->fk_product; @@ -2222,35 +2259,39 @@ class PropaleLigne var $db; var $error; - // From llx_propaldet + // From llx_propaldet var $rowid; var $fk_propal; - var $desc; // Description ligne - var $fk_product; // Id produit prédéfini + var $desc; // Description ligne + var $fk_product; // Id produit prédéfini - var $qty; - var $tva_tx; - var $subprice; - var $remise_percent; + var $qty; + var $tva_tx; + var $subprice; + var $remise_percent; var $fk_remise_except; var $rang = 0; var $marge_tx; var $marque_tx; - var $info_bits = 0; // Bit 0: 0 si TVA normal - 1 si TVA NPR - // Bit 1: 0 ligne normale - 1 si ligne de remise fixe + + var $special_code; // Bit 3: ligne en option + + var $info_bits = 0; // Bit 0: 0 si TVA normal - 1 si TVA NPR + // Bit 1: 0 ligne normale - 1 si ligne de remise fixe + var $total_ht; // Total HT de la ligne toute quantité et incluant la remise ligne var $total_tva; // Total TVA de la ligne toute quantité et incluant la remise ligne var $total_ttc; // Total TTC de la ligne toute quantité et incluant la remise ligne // Ne plus utiliser - var $remise; - var $price; + var $remise; + var $price; - // From llx_product - var $ref; // Reference produit - var $libelle; // Label produit - var $product_desc; // Description produit + // From llx_product + var $ref; // Reference produit + var $libelle; // Label produit + var $product_desc; // Description produit /** @@ -2270,7 +2311,7 @@ class PropaleLigne { $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_product, pd.description, pd.price, pd.qty, pd.tva_tx,'; $sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,'; - $sql.= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.marge_tx, pd.marque_tx, pd.rang,'; + $sql.= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.marge_tx, pd.marque_tx, pd.special_code, pd.rang,'; $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc'; $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid'; @@ -2296,10 +2337,11 @@ class PropaleLigne $this->total_ttc = $objp->total_ttc; $this->marge_tx = $objp->marge_tx; $this->marque_tx = $objp->marque_tx; + $this->special_code = $objp->special_code; $this->rang = $objp->rang; - $this->ref = $objp->product_ref; - $this->libelle = $objp->product_libelle; + $this->ref = $objp->product_ref; + $this->libelle = $objp->product_libelle; $this->product_desc = $objp->product_desc; $this->db->free($result); @@ -2348,7 +2390,7 @@ class PropaleLigne $sql.= ' (fk_propal, description, fk_product, fk_remise_except, qty, tva_tx,'; $sql.= ' subprice, remise_percent, '; $sql.= ' info_bits, '; - $sql.= ' total_ht, total_tva, total_ttc, marge_tx, marque_tx, rang)'; + $sql.= ' total_ht, total_tva, total_ttc, marge_tx, marque_tx, special_code, rang)'; $sql.= " VALUES (".$this->fk_propal.","; $sql.= " '".addslashes($this->desc)."',"; if ($this->fk_product) { $sql.= "'".$this->fk_product."',"; } @@ -2367,6 +2409,8 @@ class PropaleLigne else $sql.= ' null,'; if (isset($this->marque_tx)) $sql.= ' '.$this->marque_tx.','; else $sql.= ' null,'; + if (isset($this->special_code)) $sql.= ' '.$this->special_code.','; + else $sql.= ' 0,'; $sql.= ' '.$rangtouse; $sql.= ')'; diff --git a/mysql/tables/llx_propaldet.sql b/mysql/tables/llx_propaldet.sql index 4ca8bdd0a6a..3d6af4a7741 100644 --- a/mysql/tables/llx_propaldet.sql +++ b/mysql/tables/llx_propaldet.sql @@ -47,4 +47,5 @@ create table llx_propaldet -- -- 1 : frais de port -- 2 : ecotaxe +-- 3 : option -- \ No newline at end of file