Merge pull request #4411 from atm-ph/fix_develop_situation_invoice

FIX invoice situation
This commit is contained in:
Laurent Destailleur 2016-01-13 15:10:16 +01:00
commit 90833e5d0e
6 changed files with 2331 additions and 11 deletions

View File

@ -103,7 +103,7 @@ $extrafields = new ExtraFields($db);
// Load object
if ($id > 0 || ! empty($ref)) {
$ret = $object->fetch($id, $ref);
$ret = $object->fetch($id, $ref, '', '', $conf->global->INVOICE_USE_SITUATION);
}
// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array
@ -1163,6 +1163,18 @@ if (empty($reshook))
$object->fk_facture_source = $_POST['situations'];
$object->type = Facture::TYPE_SITUATION;
if (!empty($origin) && !empty($originid))
{
$object->origin = $origin;
$object->origin_id = $originid;
foreach ($object->lines as &$line)
{
$line->origin = $object->origin;
$line->origin_id = $line->id;
}
}
$object->fetch_thirdparty();
$object->date = $datefacture;
$object->note_public = trim($_POST['note_public']);
@ -1492,11 +1504,11 @@ if (empty($reshook))
$date_end = '';
$date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), GETPOST('date_startsec'), GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
$date_end = dol_mktime(GETPOST('date_endhour'), GETPOST('date_endmin'), GETPOST('date_endsec'), GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear'));
$description = dol_htmlcleanlastbr(GETPOST('product_desc'));
$description = dol_htmlcleanlastbr(GETPOST('product_desc') ? GETPOST('product_desc') : GETPOST('desc'));
$pu_ht = GETPOST('price_ht');
$vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
$qty = GETPOST('qty');
// Define info_bits
$info_bits = 0;
if (preg_match('/\*/', $vat_rate))
@ -2131,7 +2143,7 @@ if ($action == 'create')
$opt = $form->selectSituationInvoices(GETPOST('originid'), $socid);
print '<div class="tagtr listofinvoicetype"><div class="tagtd listofinvoicetype">';
$tmp='<input type="radio" name="type" value="5"' . (GETPOST('type') == 5 && GETPOST('originid') ? ' checked' : '');
if ($opt == ('<option value ="0" selected>' . $langs->trans('NoSituations') . '</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture')) $tmp.=' disabled';
if ($opt == ('<option value ="0" selected>' . $langs->trans('NoSituations') . '</option>') || (GETPOST('origin') && GETPOST('origin') != 'facture' && GETPOST('origin') != 'commande')) $tmp.=' disabled';
$tmp.= '> ';
$text = $tmp.$langs->trans("InvoiceSituationAsk") . ' ';
$text .= '<select class="flat" id="situations" name="situations">';
@ -3018,6 +3030,96 @@ else if ($id > 0 || ! empty($ref))
print '<td rowspan="' . $nbrows . '" colspan="2" valign="top">';
if ($object->type == Facture::TYPE_SITUATION && !empty($conf->global->INVOICE_USE_SITUATION))
{
if (count($object->tab_previous_situation_invoice) > 0 || count($object->tab_next_situation_invoice) > 0) print '<table class="nobordernopadding paymenttable" width="100%">';
if (count($object->tab_previous_situation_invoice) > 0)
{
//List of previous invoices
print '<tr class="liste_titre">';
print '<td>' . $langs->trans('ListOfPreviousSituationInvoices') . '</td>';
print '<td></td>';
if (! empty($conf->banque->enabled))
print '<td align="right"></td>';
print '<td align="right">' . $langs->trans('AmountHT') . '</td>';
print '<td align="right">' . $langs->trans('AmountTTC') . '</td>';
print '<td width="18">&nbsp;</td>';
print '</tr>';
$total_prev_ht = $total_prev_ttc = 0;
$var = true;
foreach ($object->tab_previous_situation_invoice as $prev_invoice)
{
$totalpaye = $prev_invoice->getSommePaiement();
$total_prev_ht += $prev_invoice->total_ht;
$total_prev_ttc += $prev_invoice->total_ttc;
print '<tr '.$bc [$var].'>';
print '<td>'.$prev_invoice->getNomUrl(1).'</td>';
print '<td></td>';
if (! empty($conf->banque->enabled))
print '<td align="right"></td>';
print '<td align="right">' . price($prev_invoice->total_ht) . '</td>';
print '<td align="right">' . price($prev_invoice->total_ttc) . '</td>';
print '<td align="right">'.$prev_invoice->getLibStatut(3, $totalpaye).'</td>';
print '</tr>';
$var = !$var;
}
print '<tr '.$bc [$var].'>';
print '<td colspan="2" align="right"></td>';
print '<td align="right"><b>' . price($total_prev_ht) . '</b></td>';
print '<td align="right"><b>' . price($total_prev_ttc) . '</b></td>';
print '<td width="18">&nbsp;</td>';
print '</tr>';
}
if (count($object->tab_next_situation_invoice) > 0)
{
//List of next invoices
print '<tr class="liste_titre">';
print '<td>' . $langs->trans('ListOfNextSituationInvoices') . '</td>';
print '<td></td>';
if (! empty($conf->banque->enabled))
print '<td align="right"></td>';
print '<td align="right">' . $langs->trans('AmountHT') . '</td>';
print '<td align="right">' . $langs->trans('AmountTTC') . '</td>';
print '<td width="18">&nbsp;</td>';
print '</tr>';
$total_next_ht = $total_next_ttc = 0;
$var = true;
foreach ($object->tab_next_situation_invoice as $next_invoice)
{
$totalpaye = $next_invoice->getSommePaiement();
$total_next_ht += $next_invoice->total_ht;
$total_next_ttc += $next_invoice->total_ttc;
print '<tr '.$bc [$var].'>';
print '<td>'.$next_invoice->getNomUrl(1).'</td>';
print '<td></td>';
if (! empty($conf->banque->enabled))
print '<td align="right"></td>';
print '<td align="right">' . price($next_invoice->total_ht) . '</td>';
print '<td align="right">' . price($next_invoice->total_ttc) . '</td>';
print '<td align="right">'.$next_invoice->getLibStatut(3, $totalpaye).'</td>';
print '</tr>';
$var = !$var;
}
print '<tr '.$bc [$var].'>';
print '<td colspan="2" align="right"></td>';
print '<td align="right"><b>' . price($total_next_ht) . '</b></td>';
print '<td align="right"><b>' . price($total_next_ttc) . '</b></td>';
print '<td width="18">&nbsp;</td>';
print '</tr>';
}
if (count($object->tab_previous_situation_invoice) > 0 || count($object->tab_next_situation_invoice) > 0) print '</table>';
}
print '<table class="nobordernopadding paymenttable" width="100%">';
// List of payments already done
@ -3576,7 +3678,7 @@ else if ($id > 0 || ! empty($ref))
if ($resteapayer == $object->total_ttc && empty($object->paye) && $ventilExportCompta == 0)
{
if (! $objectidnext && $object->is_last_in_cycle())
if (! $objectidnext)
{
if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->creer))
|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->invoice_advance->unvalidate)))

View File

@ -123,6 +123,16 @@ class Facture extends CommonInvoice
*/
public $situation_final;
/**
* @var array Table of previous situations
*/
public $tab_previous_situation_invoice=array();
/**
* @var array Table of next situations
*/
public $tab_next_situation_invoice=array();
/**
* Standard invoice
*/
@ -595,6 +605,8 @@ class Facture extends CommonInvoice
*/
function createFromCurrent($user,$invertdetail=0)
{
global $conf;
// Charge facture source
$facture=new Facture($this->db);
@ -612,6 +624,9 @@ class Facture extends CommonInvoice
$facture->remise_absolue = $this->remise_absolue;
$facture->remise_percent = $this->remise_percent;
$facture->origin = $this->origin;
$facture->origin_id = $this->origin_id;
$facture->lines = $this->lines; // Tableau des lignes de factures
$facture->products = $this->lines; // Tant que products encore utilise
$facture->situation_counter = $this->situation_counter;
@ -641,6 +656,20 @@ class Facture extends CommonInvoice
$this->error=$facture->error;
$this->errors=$facture->errors;
}
elseif ($this->type == self::TYPE_SITUATION && !empty($conf->global->INVOICE_USE_SITUATION))
{
$this->fetchObjectLinked('', '', $object->id, 'facture');
foreach ($this->linkedObjectsIds as $typeObject => $Tfk_object)
{
foreach ($Tfk_object as $fk_object)
{
$facture->add_object_linked($typeObject, $fk_object);
}
}
$facture->add_object_linked('facture', $this->fk_facture_source);
}
return $facid;
}
@ -923,9 +952,10 @@ class Facture extends CommonInvoice
* @param string $ref Reference of invoice
* @param string $ref_ext External reference of invoice
* @param int $ref_int Internal reference of other object
* @param bool $fetch_situation Fetch the previous and next situation in $tab_previous_situation_invoice and $tab_next_situation_invoice
* @return int >0 if OK, <0 if KO, 0 if not found
*/
function fetch($rowid, $ref='', $ref_ext='', $ref_int='')
function fetch($rowid, $ref='', $ref_ext='', $ref_int='', $fetch_situation=false)
{
global $conf;
@ -1015,6 +1045,11 @@ class Facture extends CommonInvoice
$this->location_incoterms = $obj->location_incoterms;
$this->libelle_incoterms = $obj->libelle_incoterms;
if ($this->type == self::TYPE_SITUATION && $fetch_situation)
{
$this->fetchPreviousNextSituationInvoice();
}
if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
// Retrieve all extrafield for invoice
@ -1143,6 +1178,36 @@ class Facture extends CommonInvoice
}
}
/**
* Fetch previous and next situations invoices
*
* @return void
*/
function fetchPreviousNextSituationInvoice()
{
global $conf;
$this->tab_previous_situation_invoice = array();
$this->tab_next_situation_invoice = array();
$sql = 'SELECT rowid, situation_counter FROM '.MAIN_DB_PREFIX.'facture WHERE rowid <> '.$this->id.' AND entity = '.$conf->entity.' AND situation_cycle_ref = '.(int) $this->situation_cycle_ref.' ORDER BY situation_counter ASC';
dol_syslog(get_class($this).'::fetchPreviousNextSituationInvoice ', LOG_DEBUG);
$result = $this->db->query($sql);
if ($result && $this->db->num_rows($result) > 0)
{
while ($objp = $this->db->fetch_object($result))
{
$invoice = new Facture($this->db);
if ($invoice->fetch($objp->rowid) > 0)
{
if ($objp->situation_counter < $this->situation_counter) $this->tab_previous_situation_invoice[] = $invoice;
else $this->tab_next_situation_invoice[] = $invoice;
}
}
}
}
/**
* Update database
@ -1910,6 +1975,14 @@ class Facture extends CommonInvoice
}
}
if (! $error && !$this->is_last_in_cycle())
{
if (! $this->updatePriceNextInvoice($langs))
{
$error++;
}
}
// Set new ref and define current statut
if (! $error)
{
@ -1946,6 +2019,45 @@ class Facture extends CommonInvoice
}
}
/**
* Update price of next invoice
*
* @param Translate $langs Translate object
* @return bool false if KO, true if OK
*/
function updatePriceNextInvoice(&$langs)
{
foreach ($this->tab_next_situation_invoice as $next_invoice)
{
$is_last = $next_invoice->is_last_in_cycle();
if ($next_invoice->brouillon && $is_last != 1)
{
$this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
return false;
}
$next_invoice->brouillon = 1;
foreach ($next_invoice->lines as $line)
{
$result = $next_invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent,
$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type,
$line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent,
$line->fk_unit);
if ($result < 0)
{
$this->error = $langs->trans('updatePriceNextInvoiceErrorUpdateline', $next_invoice->ref);
return false;
}
}
break; // Only the next invoice and not each next invoice
}
return true;
}
/**
* Set draft status
*
@ -2164,7 +2276,8 @@ class Facture extends CommonInvoice
$this->line=new FactureLigne($this->db);
$this->line->context = $this->context;
$this->line->situpation_percent = $situation_percent;
$this->line->fk_facture=$this->id;
$this->line->label=$label; // deprecated
$this->line->desc=$desc;
@ -2270,12 +2383,21 @@ class Facture extends CommonInvoice
include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
global $mysoc;
global $mysoc,$langs;
dol_syslog(get_class($this)."::updateline rowid=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, date_start=$date_start, date_end=$date_end, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, type=$type, fk_parent_line=$fk_parent_line pa_ht=$pa_ht, special_code=$special_code fk_unit=$fk_unit", LOG_DEBUG);
if ($this->brouillon)
{
if (!$this->is_last_in_cycle() && empty($this->error))
{
if (!$this->checkProgressLine($rowid, $situation_percent))
{
if (!$this->error) $this->error=$langs->trans('invoiceLineProgressError');
return -3;
}
}
$this->db->begin();
// Clean parameters
@ -2398,6 +2520,33 @@ class Facture extends CommonInvoice
}
}
/**
* Check if the percent edited is lower of next invoice line
*
* @param int $idline id of line to check
* @param float $situation_percent progress percentage need to be test
* @return false if KO, true if OK
*/
function checkProgressLine($idline, $situation_percent)
{
$sql = 'SELECT fd.situation_percent FROM '.MAIN_DB_PREFIX.'facturedet fd
INNER JOIN '.MAIN_DB_PREFIX.'facture f ON (fd.fk_facture = f.rowid)
WHERE fd.fk_prev_id = '.$idline.'
AND f.fk_statut <> 0';
$result = $this->db->query($sql);
if (! $result)
{
$this->error=$this->db->error();
return false;
}
$obj = $this->db->fetch_object($result);
if ($obj === null) return true;
else return $situation_percent < $obj->situation_percent;
}
/**
* Update invoice line with percentage
*
@ -3589,10 +3738,12 @@ class Facture extends CommonInvoice
*/
function get_prev_sits()
{
global $conf;
$sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . 'facture';
$sql .= ' where situation_cycle_ref = ' . $this->situation_cycle_ref;
$sql .= ' and situation_counter < ' . $this->situation_counter;
$sql .= ' AND entity = '. ($this->entity > 0 ? $this->entity : $conf->entity);
$resql = $this->db->query($sql);
$res = array();
if ($resql && $resql->num_rows > 0) {
@ -3642,9 +3793,11 @@ class Facture extends CommonInvoice
*/
function is_last_in_cycle()
{
global $conf;
if (!empty($this->situation_cycle_ref)) {
// No point in testing anything if we're not inside a cycle
$sql = 'SELECT max(situation_counter) FROM ' . MAIN_DB_PREFIX . 'facture WHERE situation_cycle_ref = ' . $this->situation_cycle_ref;
$sql = 'SELECT max(situation_counter) FROM ' . MAIN_DB_PREFIX . 'facture WHERE situation_cycle_ref = ' . $this->situation_cycle_ref . ' AND entity = ' . ($this->entity > 0 ? $this->entity : $conf->entity);
$resql = $this->db->query($sql);
if ($resql && $resql->num_rows > 0) {

View File

@ -2075,6 +2075,7 @@ abstract class CommonObject
$sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
if ($exclspec)
@ -2109,7 +2110,7 @@ abstract class CommonObject
if ($forcedroundingmode == '0') // Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
{
$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
$tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array);
$tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100));
$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
if ($diff)
{

File diff suppressed because it is too large Load Diff

View File

@ -186,6 +186,7 @@ ShowInvoice=Show invoice
ShowInvoiceReplace=Show replacing invoice
ShowInvoiceAvoir=Show credit note
ShowInvoiceDeposit=Show deposit invoice
ShowInvoiceSituation=Show situation invoice
ShowPayment=Show payment
AlreadyPaid=Already paid
AlreadyPaidBack=Already paid back
@ -301,6 +302,8 @@ WarningBillExist=Warning, one or more invoice already exist
MergingPDFTool=Merging PDF tool
AmountPaymentDistributedOnInvoice=Payment amount distributed on invoice
PaymentNote=Payment note
ListOfPreviousSituationInvoices=List of previous situation invoices
ListOfNextSituationInvoices=List of next situation invoices
# PaymentConditions
PaymentConditionShortRECEP=Immediate
@ -410,6 +413,7 @@ NoteListOfYourUnpaidInvoices=Note: This list contains only invoices for third pa
RevenueStamp=Revenue stamp
YouMustCreateInvoiceFromThird=This option is only available when creating invoice from tab "customer" of thirdparty
PDFCrabeDescription=Invoice PDF template Crabe. A complete invoice template (recommended Template)
PDFCrevetteDescription=Invoice PDF template Crevette. A complete invoice template for invoice situation
TerreNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0
MarsNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices, %syymm-nnnn for replacement invoices, %syymm-nnnn for deposit invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0
TerreNumRefModelError=A bill starting with $syymm already exists and is not compatible with this model of sequence. Remove it or rename it to activate this module.
@ -439,3 +443,11 @@ DisabledBecauseFinal=This situation is final.
CantBeLessThanMinPercent=The progress can't be smaller than its value in the previous situation.
NoSituations=No open situations
InvoiceSituationLast=Final and general invoice
PDFCrevetteSituationNumber=Situation N°%s
PDFCrevetteSituationInvoiceLineDecompte=Situation invoice - COUNT
PDFCrevetteSituationInvoiceTitle=Situation invoice
PDFCrevetteDescription=Invoice PDF template Crevette. A invoice template if you use situation invoice
PDFCrevetteSituationInvoiceLine=Situation N°%s : Inv. N°%s on %s
TotalSituationInvoice=Total situation
invoiceLineProgressError=Invoice line progress can't be egal or upper the next invoice line
updatePriceNextInvoiceErrorUpdateline=Error : update price on invoice line : %s

View File

@ -186,6 +186,7 @@ ShowInvoice=Afficher facture
ShowInvoiceReplace=Afficher facture de remplacement
ShowInvoiceAvoir=Afficher facture d'avoir
ShowInvoiceDeposit=Afficher facture d'acompte
ShowInvoiceSituation=Afficher facture de situation
ShowPayment=Afficher règlement
File=Fichier
AlreadyPaid=Déjà réglé
@ -300,6 +301,8 @@ RelatedSupplierInvoices=Factures fournisseurs liées
LatestRelatedBill=Dernière facture en rapport
WarningBillExist=Attention, une ou plusieurs factures existent déjà
MergingPDFTool=Outil de fusion de PDF
ListOfPreviousSituationInvoices=Liste des précédentes factures de situation
ListOfNextSituationInvoices=Liste des factures suivantes de situation
# PaymentConditions
PaymentConditionShortRECEP=À réception
@ -408,6 +411,7 @@ NoteListOfYourUnpaidInvoices=Remarque: Cette liste ne contient que les factures
RevenueStamp=Timbre fiscal
YouMustCreateInvoiceFromThird=Cette option n'est disponible que lorsqu'on accède à la création de facture depuis l'onglet "Client" d'un Tiers.
PDFCrabeDescription=Modèle de facture PDF complet (modèle recommandé par défaut)
PDFCrevetteDescription=Modèle de facture PDF pour les factures de situations
TerreNumRefModelDesc1=Renvoie le numéro sous la forme %syymm-nnnn pour les factures et factures de remplacement, %syymm-nnnn pour les avoirs et %syymm-nnnn pour les acomptes où yy est l'année, mm le mois et nnnn un compteur séquentiel sans rupture et sans remise à 0
MarsNumRefModelDesc1=Renvoie le numéro sous la forme %syymm-nnnn pour les factures, %syymm-nnnn pour les factures de remplacement, %syymm-nnnn pour les acomptes et %syymm-nnnn pour les avoirs où yy est l'année, mm le mois et nnnn un compteur séquentiel sans rupture et sans remise à 0
TerreNumRefModelError=Une facture commençant par $syymm existe déjà et est incompatible avec cet modèle de numérotation. Supprimez-la ou renommez-la pour activer ce module.
@ -437,3 +441,11 @@ DisabledBecauseFinal=Cette situation est la dernière
CantBeLessThanMinPercent=La progression ne peut être inférieure à la valeur de progression du point de situation précédent
NoSituations=Pas de situations ouvertes
InvoiceSituationLast=Dernière facture
PDFCrevetteSituationNumber=Situation N°%s
PDFCrevetteSituationOrderTitle=Commande d'origine
PDFCrevetteSituationInvoiceTitle=Facture de situation
PDFCrevetteSituationInvoiceLineDecompte=Facture de situation - DECOMPTE
PDFCrevetteSituationInvoiceLine=Situation N°%s : Fact. N°%s au %s
TotalSituationInvoice=Total Situtation
invoiceLineProgressError=La progression de la ligne de situation ne peut être égale ou supérieure à la ligne de situation suivante
updatePriceNextInvoiceErrorUpdateline=Erreur de mise à jour d'un prix sur la facture : %s