Merge pull request #17400 from OPEN-DSI/fix_00007

FIX Supplier invoices (deposit, multicurrency in lines)
This commit is contained in:
Laurent Destailleur 2021-04-27 18:43:41 +02:00 committed by GitHub
commit be541a2f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 155 additions and 24 deletions

View File

@ -141,7 +141,7 @@ class DiscountAbsolute
$sql .= " fsup.ref as ref_invoice_supplier_source, fsup.type as type_invoice_supplier_source";
$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as sr";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON sr.fk_facture_source = f.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fsup ON sr.fk_invoice_supplier_source = fsup.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as fsup ON sr.fk_invoice_supplier_source = fsup.rowid";
$sql .= " WHERE sr.entity IN (".getEntity('invoice').")";
if ($rowid) {
$sql .= " AND sr.rowid=".((int) $rowid);

View File

@ -36,7 +36,6 @@
include_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
@ -501,8 +500,8 @@ class FactureFournisseur extends CommonInvoice
if (count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode)
dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
foreach ($this->lines as $i => $val) {
$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code)';
$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).')';
$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).','.($this->lines[$i]->fk_remise_except > 0 ? $this->lines[$i]->fk_remise_except : 'NULL').')';
$resql_insert = $this->db->query($sql);
if ($resql_insert) {
@ -546,8 +545,8 @@ class FactureFournisseur extends CommonInvoice
$line = (object) $line;
}
$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code)';
$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).')';
$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)';
$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).','.($this->lines[$i]->fk_remise_except > 0 ? $this->lines[$i]->fk_remise_except : 'NULL').')';
$resql_insert = $this->db->query($sql);
if ($resql_insert) {
@ -797,7 +796,7 @@ class FactureFournisseur extends CommonInvoice
$this->lines = array();
$sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
$sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn ';
$sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn, f.fk_remise_except';
$sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
$sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.description as product_desc';
$sql .= ', f.fk_code_ventilation, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc';
@ -841,6 +840,7 @@ class FactureFournisseur extends CommonInvoice
$line->localtax2_type = $obj->localtax2_type;
$line->qty = $obj->qty;
$line->remise_percent = $obj->remise_percent;
$line->fk_remise_except = $obj->fk_remise_except;
$line->tva = $obj->total_tva; // deprecated
$line->total_ht = $obj->total_ht;
$line->total_ttc = $obj->total_ttc;
@ -1772,13 +1772,14 @@ class FactureFournisseur extends CommonInvoice
* @param string $ref_supplier Supplier ref
* @param string $special_code Special code
* @param int $fk_parent_line Parent line id
* @param int $fk_remise_except Id discount used
* @return int >0 if OK, <0 if KO
*/
public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = '', $date_end = '', $ventil = 0, $info_bits = '', $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = false, $array_options = 0, $fk_unit = null, $origin_id = 0, $pu_ht_devise = 0, $ref_supplier = '', $special_code = '', $fk_parent_line = 0)
public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = '', $date_end = '', $ventil = 0, $info_bits = '', $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = false, $array_options = 0, $fk_unit = null, $origin_id = 0, $pu_ht_devise = 0, $ref_supplier = '', $special_code = '', $fk_parent_line = 0, $fk_remise_except = 0)
{
global $langs, $mysoc, $conf;
dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$ventil,$info_bits,$price_base_type,$type,$fk_unit", LOG_DEBUG);
dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$ventil,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG);
include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
if ($this->statut == self::STATUS_DRAFT) {
@ -1957,6 +1958,7 @@ class FactureFournisseur extends CommonInvoice
$this->line->ventil = $ventil;
$this->line->rang = $rang;
$this->line->info_bits = $info_bits;
$this->line->fk_remise_except = $fk_remise_except;
$this->line->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
$this->line->fk_parent_line = $fk_parent_line;
@ -2677,10 +2679,10 @@ class FactureFournisseur extends CommonInvoice
$this->multicurrency_tx = 1;
$this->multicurrency_code = $conf->currency;
$xnbp = 0;
if (empty($option) || $option != 'nolines') {
// Lines
$nbp = 5;
$xnbp = 0;
while ($xnbp < $nbp) {
$line = new SupplierInvoiceLine($this->db);
$line->desc = $langs->trans("Description")." ".$xnbp;
@ -2774,7 +2776,7 @@ class FactureFournisseur extends CommonInvoice
*/
public function createFromClone(User $user, $fromid, $invertdetail = 0)
{
global $langs;
global $conf, $langs;
$error = 0;
@ -3133,6 +3135,12 @@ class SupplierInvoiceLine extends CommonObjectLine
*/
public $info_bits;
/**
* Link to line into llx_remise_except
* @var int
*/
public $fk_remise_except;
/**
* @var int ID
*/
@ -3189,7 +3197,7 @@ class SupplierInvoiceLine extends CommonObjectLine
public function fetch($rowid)
{
$sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.tva_tx';
$sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2 ';
$sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2, f.fk_remise_except';
$sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_facture_fourn, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
$sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
$sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
@ -3232,6 +3240,7 @@ class SupplierInvoiceLine extends CommonObjectLine
$this->qty = $obj->qty;
$this->remise_percent = $obj->remise_percent;
$this->fk_remise_except = $obj->fk_remise_except;
$this->tva = $obj->total_tva; // deprecated
$this->total_ht = $obj->total_ht;
$this->total_tva = $obj->total_tva;
@ -3348,6 +3357,22 @@ class SupplierInvoiceLine extends CommonObjectLine
$this->localtax2_tx = 0;
}
if (empty($this->pa_ht)) {
$this->pa_ht = 0;
}
if (empty($this->multicurrency_subprice)) {
$this->multicurrency_subprice = 0;
}
if (empty($this->multicurrency_total_ht)) {
$this->multicurrency_total_ht = 0;
}
if (empty($this->multicurrency_total_tva)) {
$this->multicurrency_total_tva = 0;
}
if (empty($this->multicurrency_total_ttc)) {
$this->multicurrency_total_ttc = 0;
}
$this->db->begin();
if (empty($this->fk_product)) {
@ -3371,6 +3396,8 @@ class SupplierInvoiceLine extends CommonObjectLine
$sql .= ", pu_ttc = ".price2num($this->pu_ttc);
$sql .= ", qty = ".price2num($this->qty);
$sql .= ", remise_percent = ".price2num($this->remise_percent);
if ($this->fk_remise_except) $sql.= ", fk_remise_except=".$this->fk_remise_except;
else $sql.= ", fk_remise_except=null";
$sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
$sql .= ", tva_tx = ".price2num($this->tva_tx);
$sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
@ -3442,7 +3469,7 @@ class SupplierInvoiceLine extends CommonObjectLine
*/
public function insert($notrigger = 0)
{
global $user, $conf;
global $user, $conf, $langs;
$error = 0;
@ -3533,7 +3560,7 @@ class SupplierInvoiceLine extends CommonObjectLine
$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
$sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
$sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
$sql .= ' fk_product, product_type, remise_percent, pu_ht, pu_ttc,';
$sql .= ' fk_product, product_type, remise_percent, fk_remise_except, pu_ht, pu_ttc,';
$sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
$sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
$sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
@ -3554,6 +3581,7 @@ class SupplierInvoiceLine extends CommonObjectLine
$sql .= ' '.(!empty($this->fk_product) ? $this->fk_product : "null").',';
$sql .= " ".$this->product_type.",";
$sql .= " ".price2num($this->remise_percent).",";
$sql .= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
$sql .= " ".price2num($this->subprice).",";
$sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
$sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
@ -3588,6 +3616,45 @@ class SupplierInvoiceLine extends CommonObjectLine
}
}
// Si fk_remise_except defini, on lie la remise a la facture
// ce qui la flague comme "consommee".
if ($this->fk_remise_except) {
$discount = new DiscountAbsolute($this->db);
$result = $discount->fetch($this->fk_remise_except);
if ($result >= 0) {
// Check if discount was found
if ($result > 0) {
// Check if discount not already affected to another invoice
if ($discount->fk_facture_line > 0) {
if (empty($noerrorifdiscountalreadylinked)) {
$this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
$this->db->rollback();
return -3;
}
} else {
$result = $discount->link_to_invoice($this->rowid, 0);
if ($result < 0) {
$this->error = $discount->error;
dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
$this->db->rollback();
return -3;
}
}
} else {
$this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
$this->db->rollback();
return -3;
}
} else {
$this->error = $discount->error;
dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
$this->db->rollback();
return -3;
}
}
if (!$error && !$notrigger) {
// Call trigger
$result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);

View File

@ -230,7 +230,66 @@ class PaiementFourn extends Paiement
$alreadypayed = price2num($paiement + $creditnotes + $deposits, 'MT');
$remaintopay = price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits, 'MT');
if ($remaintopay == 0) {
$result = $invoice->setPaid($user, '', '');
// If invoice is a down payment, we also convert down payment to discount
if ($invoice->type == FactureFournisseur::TYPE_DEPOSIT) {
$amount_ht = $amount_tva = $amount_ttc = array();
$multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
// Insert one discount by VAT rate category
require_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
$discount = new DiscountAbsolute($this->db);
$discount->fetch('', $invoice->id);
if (empty($discount->id)) { // If the invoice was not yet converted into a discount (this may have been done manually before we come here)
$discount->discount_type = 1; // Supplier discount
$discount->description = '(DEPOSIT)';
$discount->fk_soc = $invoice->socid;
$discount->fk_invoice_supplier_source = $invoice->id;
// Loop on each vat rate
$i = 0;
foreach ($invoice->lines as $line) {
if ($line->total_ht != 0) { // no need to create discount if amount is null
$amount_ht[$line->tva_tx] += $line->total_ht;
$amount_tva[$line->tva_tx] += $line->total_tva;
$amount_ttc[$line->tva_tx] += $line->total_ttc;
$multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
$multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
$multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
$i++;
}
}
foreach ($amount_ht as $tva_tx => $xxx) {
$discount->amount_ht = abs($amount_ht[$tva_tx]);
$discount->amount_tva = abs($amount_tva[$tva_tx]);
$discount->amount_ttc = abs($amount_ttc[$tva_tx]);
$discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
$discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
$discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
$discount->tva_tx = abs($tva_tx);
$result = $discount->create($user);
if ($result < 0) {
$error++;
break;
}
}
}
if ($error) {
setEventMessages($discount->error, $discount->errors, 'errors');
$error++;
}
}
// Set invoice to paid
if (!$error) {
$result = $invoice->setPaid($user, '', '');
if ($result < 0) {
$this->error = $invoice->error;
$error++;
}
}
} else {
dol_syslog("Remain to pay for invoice ".$facid." not null. We do nothing.");
}

View File

@ -986,7 +986,10 @@ if (empty($reshook)) {
$result = $srcobject->fetch(GETPOST('originid', 'int'));
// If deposit invoice - down payment with 1 line (fixed amount or percent)
if (GETPOST('type') == Facture::TYPE_DEPOSIT && in_array($typeamount, array('amount', 'variable'))) {
$typeamount = GETPOST('typedeposit', 'alpha');
if (GETPOST('type') == FactureFournisseur::TYPE_DEPOSIT && in_array($typeamount, array('amount', 'variable'))) {
$valuedeposit = price2num(GETPOST('valuedeposit', 'alpha'), 'MU');
// Define the array $amountdeposit
$amountdeposit = array();
if (!empty($conf->global->MAIN_DEPOSIT_MULTI_TVA)) {
@ -1099,9 +1102,7 @@ if (empty($reshook)) {
$subprice_diff = $object->lines[0]->subprice - $diff / (1 + $object->lines[0]->tva_tx / 100);
$object->updateline($object->lines[0]->id, $object->lines[0]->desc, $subprice_diff, $object->lines[0]->qty, $object->lines[0]->remise_percent, $object->lines[0]->date_start, $object->lines[0]->date_end, $object->lines[0]->tva_tx, 0, 0, 'HT', $object->lines[0]->info_bits, $object->lines[0]->product_type, 0, 0, 0, $object->lines[0]->pa_ht, $object->lines[0]->label, 0, array(), 100);
}
}
if ($result > 0) {
} elseif ($result > 0) {
$lines = $srcobject->lines;
if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
$srcobject->fetch_lines();
@ -1222,7 +1223,7 @@ if (empty($reshook)) {
// Edit line
$db->begin();
$object->fetch($id);
if (! $object->fetch($id) > 0) dol_print_error($db);
$object->fetch_thirdparty();
$tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
@ -1271,7 +1272,7 @@ if (empty($reshook)) {
$localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $object->thirdparty);
$remise_percent = price2num(GETPOST('remise_percent'), 2);
$pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), 'CU');
$pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), 'MU');
// Extrafields Lines
$extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
@ -1337,7 +1338,7 @@ if (empty($reshook)) {
$qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS');
$remise_percent = price2num(GETPOST('remise_percent'.$predef), 2);
$price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
$price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'MU', 2);
// Extrafields
$extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
@ -1964,7 +1965,7 @@ if ($action == 'create') {
print $desc;
print '</div></div>';
if ((empty($origin)) || ((($origin == 'propal') || ($origin == 'commande')) && (! empty($originid)))) {
if (empty($origin) || ($origin == 'order_supplier' && !empty($originid))) {
// Deposit - Down payment
if (empty($conf->global->INVOICE_DISABLE_DEPOSIT)) {
print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
@ -2004,7 +2005,7 @@ if ($action == 'create') {
print '<td>';
print $desc;
print '</td>';
if (($origin == 'propal') || ($origin == 'commande')) {
if ($origin == 'order_supplier') {
print '<td class="nowrap" style="padding-left: 15px">';
$arraylist = array(
'amount' => $langs->transnoentitiesnoconv('FixAmount', $langs->transnoentitiesnoconv('Deposit')),

View File

@ -440,3 +440,5 @@ ALTER TABLE llx_payment_donation ADD COLUMN ext_payment_id varchar(128) AFTER n
UPDATE llx_const SET value = 'github' WHERE __DECRYPT('name')__ = 'MAIN_BUGTRACK_ENABLELINK' AND __DECRYPT('value')__ = 1;
ALTER TABLE llx_facture_fourn_det ADD COLUMN fk_remise_except integer DEFAULT NULL after remise_percent;
ALTER TABLE llx_facture_fourn_det ADD UNIQUE INDEX uk_fk_remise_except (fk_remise_except, fk_facture_fourn);

View File

@ -23,6 +23,7 @@
ALTER TABLE llx_facture_fourn_det ADD INDEX idx_facture_fourn_det_fk_facture (fk_facture_fourn);
ALTER TABLE llx_facture_fourn_det ADD INDEX idx_facture_fourn_det_fk_product (fk_product);
ALTER TABLE llx_facture_fourn_det ADD INDEX idx_facture_fourn_det_fk_code_ventilation (fk_code_ventilation);
ALTER TABLE llx_facture_fourn_det ADD UNIQUE INDEX uk_fk_remise_except (fk_remise_except, fk_facture_fourn);
ALTER TABLE llx_facture_fourn_det ADD CONSTRAINT fk_facture_fourn_det_fk_facture FOREIGN KEY (fk_facture_fourn) REFERENCES llx_facture_fourn (rowid);
ALTER TABLE llx_facture_fourn_det ADD CONSTRAINT fk_facture_fourn_det_fk_unit FOREIGN KEY (fk_unit) REFERENCES llx_c_units (rowid);

View File

@ -31,6 +31,7 @@ create table llx_facture_fourn_det
pu_ttc double(24,8), -- unit price with tax
qty real, -- quantity of product/service
remise_percent real DEFAULT 0, -- % de la remise ligne (exemple 20%)
fk_remise_except integer NULL, -- Lien vers table des remises fixes
vat_src_code varchar(10) DEFAULT '', -- Vat code used as source of vat fields. Not strict foreign key here.
tva_tx double(6,3), -- TVA taux product/service
localtax1_tx double(6,3) DEFAULT 0, -- localtax1 rate