Merge branch '250826-e-einvoice' into unstable

* 250826-e-einvoice: (29 commits)
  Update VENDOR : remove useless
  Factur-X norme 3 (May 15th, 2025)
  E-INVOICE : if upload an XML file than the embedded document will be saved separately, if there is no embedded document a standard document with info from XML will be generated
  E-INVOICE , when loading a XML , the file is splitted into a PDF and XML
  XMLInvoiceReader : extract information from XML
  E-INVOICE : corrige PriceAmount
  E-INVOICE : show the XML to download
  Database: upload file function
  E-INVOICE : export XML if exists from ledger_detail_bottom
  E-INVOICE : create standard invoice if not convert
  E-INVOICE: small bugs UBL21
  E-INVOICE: negative amount
  e-invoice : add communication , bank and buyer reference
  e-invoice : display errors if cannot be created
  Typo : space quantity
  PHPUNIT : adapt 12.3
  E-INVOICE: check data before generating E-INVOICE: add missing for Belgium : BuyerReference and Due_Date
  E-INVOICE: add code quantity
  E-INVOICE : add AdditionalDocument
  XMLInvoice adapt currency
  ...
This commit is contained in:
sparkyx 2025-10-06 09:01:38 +02:00
commit 9898434767
169 changed files with 3614 additions and 15659 deletions

View file

@ -0,0 +1,387 @@
<?php
/*
* This file is part of NOALYSS.
*
* NOALYSS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* NOALYSS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NOALYSS; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Copyright Author Dany De Bontridder danydb@aevalys.eu 28/08/25
use Noalyss\XMLDocument\XMLInvoice_Reader;
/**
* @file
* @brief Document used in accountancy : invoice , credit note, ...It is
* a specialization of Document used in Follow-UP
*/
/**
* @class
* @brief Document used in accountancy : invoice , credit note, ... It is
* a specialization of Document used in Follow-UP.
* property :
* - d_id JRN.JR_ID
* - d_name name Receipt number
- d_description Comment of the operation
- d_mimetype mimetype of the document JRN.JR_PJ_TYPE
- d_filename filename JRN.JR_PJ_NAME
- document_xml oid of the XML invoice (including PDF) JRN.JR_DOCUMENT_XML
- d_lob = JRN.JR_PJ
*
*/
class Acc_Document extends Document {
private $document_xml; ///< $document_xml (oid) XML document e-invoice
public function get_document_xml() {
return $this->document_xml;
}
public function set_document_xml($document_xml) {
$this->document_xml = $document_xml;
return $this;
}
/**
* @brief constructor
* @param $cn \Database
* @param $jr_id (int) JRN.JRID will be in d_id
*/
function __construct($cn, $jr_id=0)
{
$this->db=$cn;
$this->set_id($jr_id);
// counter for MARCH_NEXT
$this->counter=0;
}
/**
* @brief set_id fill up d_filename, d_mimetype,d_lob,d_description,jr_pj_number
*/
function set_id($jr_id) {
$this->d_id=$jr_id;
if ( $jr_id == 0 ){
return $this;
}
$row = $this->db->get_row("select jr_comment
,jr_pj
,jr_pj_name
,jr_pj_type
,jr_pj_number
,jr_document_xml
from jrn
where
jr_id=$1", [$this->d_id]);
if ( empty ($row)) {
return $this;
}
$this->d_name=$row['jr_pj_number'];
$this->d_description=$row['jr_comment'];
$this->d_mimetype=$row['jr_pj_type'];
$this->d_filename=$row['jr_pj_name'];
$this->d_lob=$row['jr_pj'];
$this->document_xml=$row['jr_document_xml'];
return $this;
}
/**
* @brief save the file into DB, will create a large object if there
* is no document to replace. It will change the d_filename, d_mimetype
*
* @param $d_filename (string) full path to the file to load into DB
*
* @returns false if d_id = 0 or the file doesn't exist, true for success
*/
function update($filename) {
if ($this->d_id == 0) return false;
if ( ! file_exists($filename)) return false;
$this->db->start();
$this->d_mimetype= mime_content_type($filename);
$this->d_filename= basename($filename);
if ( $this->d_lob == "") {
$this->db->lo_unlink($this->d_lob);
}
$this->d_lob=$this->db->lo_import($filename);
$this->db->exec_sql(
"update jrn set jr_pj=$1,jr_pj_name=$2,jr_pj_type=$3
where jr_id=$4",
[$this->d_lob,$this->d_filename,$this->d_mimetype,$this->d_id]
);
$this->db->commit();
return true;
}
/**
* @brief for FACTURX we replace the PDF save in DB by this one, always
* a PDF (since it is a FACTURX document)
*/
function replace_receipt($new_oid)
{
if ($this->d_lob != "")
{
$this->db->lo_unlink($this->d_lob);
}
$this->d_lob=$new_oid;
$this->db->exec_sql("
update jrn
set
jr_pj = $1
,jr_pj_name =$2
,jr_pj_type =$3
where jr_id=$4
",
[$this->d_lob
,$this->d_filename
,$this->d_mimetype
,$this->d_id]
);
}
/**
* @brief save the Large Object $oid in the column JRN.JR_DOCUMENT_XML
* @param $oid( OID) PostgreSQL Object ID
*/
function update_document_xml($oid){
$this->db->exec_sql("update jrn set jr_document_xml=$1 where
jr_id=$2",[
$oid,
$this->d_id
]);
}
/**
* @brief create the invoice and saved it as attachment to the
* operation,
* @param $p_array is normally the $_POST
@verbatim
Array
(
[ledger_type] => VEN
[gDossier] => x
[nb_item] => 1 (number of item used to numerate e_marchX, e_quantX ,...)
[p_jrn] => 2
[p_jrn_predef] => 2
[jrn_type] => VEN or ACH
[e_date] => 10.06.2025 (date)
[e_ech] => limite date
[e_client] => CLIENT
[e_pj] => 25.827
[e_pj_suggest] => 25.827
[e_comm] => E-INVOICE
[p_currency_code] => 0
[p_currency_rate] => 1 (rate if 1 for EURO)
[jrn_note_input] =>
[e_march0] => 7DVINV
[e_march0_label] => Label of the operation
[e_march0_price] => 10.0000
[e_quant0] => 1.0000
[htva_march0] => 10
[e_march0_tva_id] => 1
[e_march0_tva_amount] => 2.1
[tva_march0] => 2.1
[tvac_march0] => 12.1
...
[mp_date] => (dd.mm.yyyy date of payment)
[acompte] => 0 (amount to deduce as advance payment)
[e_comm_paiement] => (string : comment of the payment)
[e_mp] => 0 (method of payment it is the XX in e_mp_qcode_XX)
[e_mp_qcode_16] => Banque 1
[e_mp_qcode_17] => Banque 2
[view_invoice] => Enregistrer
[gen_doc] => int DOCUMENT_MODELE.MD_ID , document template to use
)
* @endverbatim
* @todo rewrite code : remove extract and +SQL value
* @returns void
*/
function create_document($internal, $p_array) {
$this->f_id = $p_array['e_client'];
// var md_id (int) DOCUMENT_MODELE.MD_ID
$this->md_id = $p_array['gen_doc'];
// var ag_id == 0 fake follow-up
$this->ag_id = 0;
// var e_pj (string) receipt nb
$p_array['e_pj'] = $this->db->get_value("select jr_pj_number from jrn where jr_id=$1"
, [$this->d_id]);
$filename = "";
// generate the document and set d_lob,d_mimetype,
$this->generate($p_array, $p_array['e_pj']);
// Move the document to accountancy (table JRN),
$this->moveDocumentACC($internal);
// Update the comment with invoice number, if the comment is empty
if (!isset($p_array['e_comm']) || noalyss_strlentrim($p_array['e_comm']) == 0) {
$sql = "update jrn set jr_comment=' document " . $this->d_number . "' where jr_internal=$1";
$this->db->exec_sql($sql, [$internal]);
}
}
/**
* @brief export the file to the file system and complet $this->d_mimetype, d_filename and
* @param $destination_file (string) full path to document
* @return bool false for failure and string (the full path_name) for success
*/
function export_file($destination_file) {
if (empty($this->d_filename)) {
return false;
}
$this->db->start();
if ($this->db->lo_export($this->d_lob, $destination_file) == false) {
record_log("ACD122. cannot export");
$this->db->commit();
return false;
}
$this->db->commit();
return $destination_file;
}
/**
* \brief Save a "piece justificative" , the name must be a receipt. If it
* is a XML document, split it into 2 parts : PDF and XML
* the PDF be stored in JR_PJ and the XML into JR_DOCUMENT_XML
*
*
* \return $oid of the lob file if success null if a error occurs
*
*/
function save_receipt()
{
$this->db->start();
/**
* pj is the $_FILES key
*/
$a_file= $this->db->upload('pj',only_oid:false);
if ($a_file == false) {
return false;
}
$oid=$a_file['oid'];
// Remove old document if any
$old_oid = $this->db->get_value("select jr_pj from jrn where jr_id=$1"
,[$this->d_id]);
if ( $old_oid != "")
{
$this->db->lo_unlink( $old_oid);
}
// if there is a e-invoice in XML
if ( $_FILES['pj']['type'] == 'text/xml'
|| $_FILES['pj']['type'] == 'application/xml'
)
{
// save the XML
$this->db->exec_sql("update jrn set jr_document_xml = $1
where
jr_id=$2",
[$oid,$this->d_id]);
$xmlreader= XMLInvoice_Reader::build_from_file($a_file['filename']);
//@var $embedded_file (array) keys = filecontent: binary data
//,mimecode mimetype and filename (string)
try
{
$embedded_file=$xmlreader->get_embedded_document();
if ($embedded_file == false)
{
$embedded_file=array();
// create a PDF with standard information
$pdf=$xmlreader->to_pdf($this->db);
$file_oid=$this->db->lo_write($pdf->Output("S"));
$embedded_file['filename']="invoice.pdf";
$embedded_file['mimecode']="application/pdf";
}
else
{
$file_oid=$this->db->lo_write($embedded_file['filecontent']);
if ( $file_oid == false )
{
// create a PDF with standard information
$pdf=$xmlreader->to_pdf($this->db);
$file_oid=$this->db->lo_write($pdf->Output("S"));
}
}
//@var $file_oid OID of the large object saved in DB
$this->d_name=$embedded_file['filename'];
$this->d_description=$embedded_file['filename'];
$this->d_lob=$file_oid;
$this->d_mimetype=$embedded_file['mimecode'];
// save extracted document into DB
$this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2,
jr_pj_type=$3 where jr_id=$4",
array(
$this->d_lob
, $this->d_name
, $this->d_description
, $this->d_id
)
);
return $file_oid;
} catch (\Exception $e ) {
\record_log($e);
// if exception is not too many document or document corrupted
// then rethrow the exception
if ( !in_array(e->getCode(),[110,116]) )
{
throw new \Exception("X281 ",281,$e);
}
}
}
// save new document
$this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2,
jr_pj_type=$3 where jr_id=$4",
array(
$oid
, $_FILES['pj']['name']
, $_FILES['pj']['type']
, $this->d_id
)
);
$this->d_name=$_FILES['pj']['name'];
$this->d_description=$_FILES['pj']['name'];
$this->d_lob=$oid;
$this->d_mimetype=$_FILES['pj']['type'];
$this->db->commit();
return $oid;
}
/**
* @brief return a string with a link download XML or an empty string
* if there is no XML to download
*/
function link_download_xml():string
{
$xml_oid=$this->db->get_value("select jr_document_xml from jrn where jr_id=$1",
[$this->d_id]);
if ($xml_oid == "") { return "";}
$url= "export.php?".http_build_query(
[
"gDossier"=>\Dossier::id(),
"jr_id"=>$this->d_id,
"act"=>'RAW:xml-invoice'
]);
$r = sprintf('<a class="mtitle line" href="%s">',$url);
$r .= _("XML")
.'<i class="icon-download">'
.'</i>'
.'</a>';
return $r;
}
}

View file

@ -1610,6 +1610,7 @@ class Acc_Ledger extends jrn_def_sql
$acc_end->currency_rate=$currency_rate;
$acc_end->currency_rate_ref=$currency_rate_ref->get_rate();
// @var $jr_id (int) JRN.JR_ID
$jr_id=$acc_end->insert_jrn();
$this->jr_id=$jr_id;
@ -1651,7 +1652,8 @@ class Acc_Ledger extends jrn_def_sql
*/
if (isset($_FILES["pj"]))
{
$this->db->save_receipt($seq);
$acc_document=new Acc_Document($this->db, $jr_id);
$acc_document->save_receipt();
}
/*----------------------------------------------
* Save the note
@ -1973,31 +1975,22 @@ class Acc_Ledger extends jrn_def_sql
}
/**
* @brief create the invoice and saved it as attachment to the
* operation,
* @brief alias for Acc_Document->create_document
* @param $internal is the internal code
* @param $p_array is normally the $_POST
* @todo rewrite code : remove extract and +SQL value
* \return a string
@see Acc_Document::create_document
* @return a string
*/
function create_document($internal, $p_array)
{
$doc=new Document($this->db);
$doc->f_id=$p_array['e_client'];
$doc->md_id=$p_array['gen_doc'];
$doc->ag_id=0;
$p_array['e_pj']=$this->pj;
$filename="";
$doc->Generate($p_array, $p_array['e_pj']);
// Move the document to the jrn
$doc->moveDocumentPj($internal);
// Update the comment with invoice number, if the comment is empty
if (!isset($p_array['e_comm'])||noalyss_strlentrim($p_array['e_comm'])==0)
{
$sql="update jrn set jr_comment=' document ".$doc->d_number."' where jr_internal=$1";
$this->db->exec_sql($sql,[$internal]);
$id=$this->db->get_value('select jr_id from jrn where jr_internal=$1',
[$internal]);
if ( $id == "") {
return;
}
return h($doc->d_name.' ('.$doc->d_filename.')');
$acc_document=new Acc_Document($this->db,$id);
$acc_document->create_document($internal,$p_array);
return h($acc_document->d_name.' ('.$acc_document->d_filename.')');
}
/**

View file

@ -983,6 +983,7 @@ class Acc_Ledger_Fin extends Acc_Ledger
throw new Exception (_("Erreur de balance"),EXC_BALANCE);
// $acc_operation->update_receipt();
$this->jr_id=&$jr_id;
$this->db->exec_sql('update jrn set jr_pj_number=$1 where jr_id=$2', array($acc_operation->pj, $jr_id));
$internal=$this->compute_internal_code($seq);
@ -1073,21 +1074,18 @@ class Acc_Ledger_Fin extends Acc_Ledger
$class=($i%2==0)?' class="even" ':' class="odd" ';
$ret.=tr($row, $class);
if ($i==0)
if ($i==0 && isset($_FILES['pj']) )
{
// first record we upload the files and
// keep variable to update other row of jrn
if (isset($_FILES['pj']))
$oid=$this->db->save_receipt($seq);
$acc_document=new Acc_Document($this->db,$jr_id);
$oid=$acc_document->save_receipt();
}
else
elseif ($oid != 0 )
{
if ($oid!=0)
{
$this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2,
jr_pj_type=$3 where jr_grpt_id=$4",
array($oid, $_FILES['pj']['name'], $_FILES['pj']['type'], $seq));
}
$this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2,
jr_pj_type=$3 where jr_grpt_id=$4",
array($oid, $_FILES['pj']['name'], $_FILES['pj']['type'], $seq));
}
} // for nbitem
// increment pj

View file

@ -29,9 +29,86 @@ require_once NOALYSS_INCLUDE.'/lib/ac_common.php';
/*!
* \class Acc_Ledger_Purchase
* \brief Handle the ledger of purchase,
*
*
** @brief : input, confirm and save new operations in edger of purchase
the $_POST data is an array with these keys
@code
Array
(
// =====================
// ANALYTIC PART
// =====================
[pa_id] => Array
(
[0] => 1
)
[op] => Array
(
[0] => 0
)
[amount_t0] => 10
[hplan] => Array
(
[0] => Array
(
[0] => -1
)
)
[val] => Array
(
[0] => Array
(
[0] => 10
)
)
// =====================
// SALES DATA
// =====================
[e_client] => QuickCode supplier
[nb_item] => number of items (lines of invoice)
[p_jrn] => JRN_DEF.JRN_DEF_ID id of the ledger
[jrn_note_input] => Note JRN_NOTE.N_TEXT
[mt] => 1759130008.8134
[p_currency_rate] => Currency Rate
[p_currency_code] => Currency Code
[e_comm] => Description of invoice
[e_date] => date invoice
[e_ech] => limit date
[e_pj] => Receipt number
[e_pj_suggest] => suggested receipt number
[e_mp] => payment means (
[jrn_type] => Type of ledger (always ACH)
//---------------------------------------------
// For each invoice line
//---------------------------------------------
[e_march0] => QuickCode of the item
[e_march0_label] => label
[e_march0_price] => unit price
[e_march0_tva_id] => VAT ID
[e_march0_tva_amount] => amount of VAT
[e_quant0] => quantity of item
//========================
// MISC
//========================
[repo] => 1 (repository)
[gen_invoice] => on (it is asked to generate an invoice
[gen_doc] => Document template id
[bon_comm] => JRN_INFO.
[other_info] = JRN_INFO.>
[opd_name] => Name of operation template
[od_description] => Description of operation template
[reverse_date] => if reverse is asked
[ext_label] => Label for revese operation
[jr_optype] => Type of operation NOR:Normal,, EXT; reverse, ..
)
@endcode
*/
class Acc_Ledger_Purchase extends Acc_Ledger
{
@ -978,7 +1055,9 @@ class Acc_Ledger_Purchase extends Acc_Ledger
if ( isset ($_FILES))
{
if ( sizeof($_FILES) != 0 )
$this->db->save_receipt($seq);
$acc_document=new \Acc_Document($this->db, $this->jr_id);
$acc_document->save_receipt();
$this->doc=HtmlInput::show_receipt_document($this->jr_id,h($_FILES['pj']['name']));
}
$str_file="";
/* Generate an document and save it into the database (Note de frais only)

View file

@ -28,9 +28,87 @@ require_once NOALYSS_INCLUDE.'/lib/user_common.php';
require_once NOALYSS_INCLUDE.'/lib/ac_common.php';
/*!
* \brief Handle the ledger of sold,
*
* @exception throw an exception is something is wrong
* @class Acc_Ledger_Sale
* @brief : input, confirm and save new operations in edger of sales
the $_POST data is an array with these keys
@code
Array
(
// =====================
// ANALYTIC PART
// =====================
[pa_id] => Array
(
[0] => 1
)
[op] => Array
(
[0] => 0
)
[amount_t0] => 10
[hplan] => Array
(
[0] => Array
(
[0] => -1
)
)
[val] => Array
(
[0] => Array
(
[0] => 10
)
)
// =====================
// SALES DATA
// =====================
[e_client] => QuickCode customer
[nb_item] => number of items (lines of invoice)
[p_jrn] => JRN_DEF.JRN_DEF_ID id of the ledger
[jrn_note_input] => Note JRN_NOTE.N_TEXT
[mt] => 1759130008.8134
[p_currency_rate] => Currency Rate
[p_currency_code] => Currency Code
[e_comm] => Description of invoice
[e_date] => date invoice
[e_ech] => limit date
[e_pj] => Receipt number
[e_pj_suggest] => suggested receipt number
[e_mp] => payment means (
[jrn_type] => Type of ledger (always VEN)
//---------------------------------------------
// For each invoice line
//---------------------------------------------
[e_march0] => QuickCode of the item
[e_march0_label] => label
[e_march0_price] => unit price
[e_march0_tva_id] => VAT ID
[e_march0_tva_amount] => amount of VAT
[e_quant0] => quantity of item
//========================
// MISC
//========================
[repo] => 1 (repository)
[gen_invoice] => on (it is asked to generate an invoice
[gen_doc] => Document template id
[bon_comm] => JRN_INFO.
[other_info] = JRN_INFO.>
[opd_name] => Name of operation template
[od_description] => Description of operation template
[reverse_date] => if reverse is asked
[ext_label] => Label for revese operation
[jr_optype] => Type of operation NOR:Normal,, EXT; reverse, ..
)
@endcode
*/
class Acc_Ledger_Sale extends Acc_Ledger {
@ -698,17 +776,7 @@ class Acc_Ledger_Sale extends Acc_Ledger {
where j_id in (select j_id from jrnx where j_grpt=$2)'
, array($internal, $seq));
/* Save the attachment or generate doc */
if (isset($_FILES['pj'])) {
if (noalyss_strlentrim($_FILES['pj']['name']) != 0)
$this->db->save_receipt($seq);
else
/* Generate an invoice and save it into the database */
if (isset($_POST['gen_invoice'])) {
$file = $this->create_document($internal, $p_array);
$this->doc=HtmlInput::show_receipt_document($this->jr_id,h($file));
}
}
//----------------------------------------
// Save the payer
//----------------------------------------
@ -932,6 +1000,7 @@ class Acc_Ledger_Sale extends Acc_Ledger {
$r.='<td>' . _('Numéro Pièce') .$span.'</td><td>'. hb($this->pj) . '</td>';
}
}
$e_comm=($e_comm == "")?_('Facture')." $e_pj":$e_comm;
$r.='</tr>';
$r.='<tr>';
$r.='<td> ' . _('Date') . '</td><td> ' . hb($e_date) . '</td>';
@ -954,7 +1023,7 @@ class Acc_Ledger_Sale extends Acc_Ledger {
$r.='</tr>';
$r.='<tr>';
$r.='<td> ' . _('Client') . '</td><td> ' . hb($e_client . ':' . $client_name) . '</td>';
$r.='<td> ' . _('Client') . '</td><td> ' . HtmlInput::card_detail($e_client).":".hb( $client_name) . '</td>';
$r.='</tr>';
$r.='</table>';
$r.='<pre>'._('Note').' '.h($p_array['jrn_note_input']).'</pre>';
@ -1296,7 +1365,8 @@ EOF;
return $r;
}
/*!\brief the function extra info allows to
/*!
* \brief the function extra info allows to
* - add a attachment
* - generate an invoice
* - insert extra info
@ -1304,31 +1374,35 @@ EOF;
*/
public function extra_info() {
$r = '<div id="facturation_div_id" style="height:185px;height:10rem">';
$r = '<div id="facturation_div_id" style="display:flex;height:185px;height:10rem">';
// check for upload piece
$file = new IFile();
$file->table = 0;
$file->setAlertOnSize(true);
$r.='<p class="decale">';
// add a receipt
$r.=_("Ajoutez une pièce justificative ");
$r.=$file->input("pj", "");
if ($this->db->count_sql("select md_id,md_name from document_modele where md_affect='VEN' ") > 0) {
$r.=_('ou générer une facture') . ' <input type="checkbox" name="gen_invoice" CHECKED>';
// We propose to generate the invoice and some template
$doc_gen = new ISelect();
$doc_gen->name = "gen_doc";
$doc_gen->value = $this->db->make_array(
"select md_id,md_name " .
" from document_modele where md_affect='VEN' order by 2");
$r.=$doc_gen->input() . '<br>';
}
//------------------------------------------------
// Propose to generate an invoice
//------------------------------------------------
$r.=_('ou générer une facture') . ' <input type="checkbox" name="gen_invoice" CHECKED>';
// We propose to generate the invoice and some template
$doc_gen = new ISelect();
$doc_gen->name = "gen_doc";
$doc_gen->value = $this->db->make_array(
"select md_id,md_name " .
" from document_modele where md_affect='VEN' ".
" union select -2,'"._("0 - Facture PDF Standard")."' ".
" order by 2");
$r.=$doc_gen->input() . '<br>';
$r.='<br>';
$obj = new IText();
$r.=_('Numero de bon de commande : ') . $obj->input('bon_comm') . '<br>';
$r.=_('Communication ou autre information : ') . $obj->input('other_info') . '<br>';
$r.=_('Numero de bon de commande') . $obj->input('bon_comm') . '<br>';
$r.=_('Communication') . $obj->input('other_info') . '<br>';
$r.='</p>';
$r.='</div>';
return $r;

View file

@ -91,9 +91,9 @@ class Acc_Operation
jr_id {$this->jr_id}
jr_optype {$this->jr_optype}
amount {$this->amount}
currency_rate {$this->amount}
currency_rate_ref {$this->amount}
currency_id {$this->amount}
currency_rate {$this->currency_rate}
currency_rate_ref {$this->currency_rate_ref}
currency_id {$this->currency_id}
]
EOF;
return $r;
@ -1031,11 +1031,43 @@ EOF;
/**
* @class Acc_Detail
* @brief Contains the detail of an operation Acc_Operation
* propery :
* - $det
- jr_id PKfrom table JRN
- jr_def_id id of the ledger (FK to - _DEF- _DEF_ID)from table JRN
- jr_montant AMOUNT of the operationfrom table JRN
- jr_comment COMMENT from table JRN
- jr_date DATEfrom table JRN
- jr_grpt_id CODE to group - X rowsfrom table JRN
- jr_internal INTERNAL CODEfrom table JRN
- jr_tech_date DATE OF CHANGEfrom table JRN
- jr_tech_per FK TO PARAM_PERIODEP_IDfrom table JRN
- jrn_ech from table JRN
- jr_ech DATE LIMIT OF PAYMENTfrom table JRN
- jr_rapt from table JRN
- jr_echfrom table JRN
- jr_validfrom table JRN
- jr_opid from table JRN
- jr_c_opidfrom table JRN
- jr_pj OID OF THE DOCUMENTfrom table JRN
- jr_pj_name NAME OF THE DOCUMENTfrom table JRN
- jr_pj_typefrom table JRN
- jr_pj_number RECEIPT NBfrom table JRN
- jr_mt INTERNAL CODEfrom table JRN
- jr_raptfrom table JRN
- jr_date_paid DATE OF PAYMENTfrom table JRN
- jr_optype TYPE OF OPERATION NOR = NORMAL OPE=OPENING EXT=EXTOURNEfrom table JRN
- currency_id FK TO CURRENCYIDfrom table JRN
- currency_rate amountfrom table JRN
- currency_rate_ref amount in CURRENT_HISTORYCH_VALUEfrom table JRN
* - note from table JRN_NOTE
- $jr_id JRN.JR_ID
- $info
*/
class Acc_Detail extends Acc_Operation
{
public $det;
public $jr_id;
public $det;//!< Object with columns from JRN
public $jr_id;//! $jr_id (int) JRN.JR_ID
public $info;
function __construct($p_cn,$p_jrid=0)
@ -1050,10 +1082,34 @@ class Acc_Detail extends Acc_Operation
*/
function get()
{
$sql="SELECT jr_id, jr_def_id, jr_montant, jr_comment, jr_date, jr_grpt_id,
jr_internal, jr_tech_date, jr_tech_per, jrn_ech, jr_ech, jr_rapt,jr_ech,
jr_valid, jr_opid, jr_c_opid, jr_pj, jr_pj_name, jr_pj_type,
jr_pj_number, jr_mt,jr_rapt,jr_date_paid,jr_optype,currency_id,currency_rate,currency_rate_ref
$sql="SELECT jr_id
, jr_def_id
, jr_montant
, jr_comment
, jr_date
, jr_grpt_id
, jr_internal
, jr_tech_date
, jr_tech_per
, jrn_ech
, jr_ech
, jr_rapt
,jr_ech
, jr_valid
, jr_opid
, jr_c_opid
, jr_pj
, jr_pj_name
, jr_pj_type,
jr_pj_number
, jr_mt
,jr_rapt
,jr_date_paid
,jr_optype
,currency_id
,currency_rate
,currency_rate_ref
,jr_document_xml
FROM jrn where jr_id=$1";
$array=$this->db->get_array($sql,array($this->jr_id));
if ( count($array) == 0 ) throw new Exception('Aucune ligne trouvée');
@ -1363,4 +1419,5 @@ class Acc_Fin extends Acc_Detail
return $array;
}
}

View file

@ -30,7 +30,9 @@
"label"=>"tva_label",
"rate"=>"tva_rate",
"comment"=>"tva_comment",
"account"=>"tva_poste");
"account"=>"tva_poste"
tva_peppol_code
* );
*/
class Acc_Tva
@ -42,7 +44,9 @@ class Acc_Tva
"account"=>"tva_poste",
"both_side"=>'tva_both_side',
'tva_reverse_account'=>'tva_reverse_account',
'tva_code'=>'tva_code');
'tva_code'=>'tva_code',
"tva_peppol_code"=>"tva_peppol_code"
);
public $tva_id,
$tva_label,
$tva_rate,
@ -50,16 +54,18 @@ class Acc_Tva
$tva_poste,
$tva_both_side,
$tva_code,
$tva_reverse_account;
$tva_reverse_account,
$tva_peppol_code
;
private $cn; //!< Database connection
private Tva_Rate_SQL $tva_rate_sql;
function __construct ($p_init,$p_tva_id=-1)
function __construct (Database $cn,$p_tva_id=-1)
{
$this->cn=$p_init;
$this->tva_rate_sql=new Tva_Rate_SQL($p_init,$p_tva_id);
$this->cn=$cn;
$this->tva_rate_sql=new Tva_Rate_SQL($cn,$p_tva_id);
$this->tva_id=$p_tva_id;
$this->tva_label=&$this->tva_rate_sql->tva_label;
$this->tva_rate=&$this->tva_rate_sql->tva_rate;
@ -68,6 +74,7 @@ class Acc_Tva
$this->tva_both_side=&$this->tva_rate_sql->tva_both_side;
$this->tva_code=&$this->tva_rate_sql->tva_code;
$this->tva_reverse_account=&$this->tva_rate_sql->tva_reverse_account;
$this->tva_peppol_code=&$this->tva_rate_sql->tva_peppol_code;
}
/**

View file

@ -699,4 +699,18 @@ class Card_Property
}
return null;
}
/**
* @brief returns the property value of a card without creating a card
* @param \Database $conx
* @param $card_id (int) FICHE.F_ID
* @param $property_id (int) ad_value
* @return string or false if nothing was found
*/
static function get_attribute(\Database $conx, $card_id,$property_id)
{
$r=$conx->get_value("select ad_value from fiche_detail where
ad_id = $1 and f_id=$2",[$property_id,$card_id]);
if ( $conx->count() == 0) { return false;}
return $r;
}
}

View file

@ -81,34 +81,6 @@ class Database extends DatabaseCore
. ",dbname = ".$this->get_dbname()
. "]";
}
/***
* \brief Save a "piece justificative" , the name must be pj
*
* \param $seq jr_grpt_id
* \return $oid of the lob file if success
* null if a error occurs
*
*/
function save_receipt($seq)
{
$oid = $this->upload('pj');
if ($oid == false) {
return false;
}
// Remove old document
$ret = $this->exec_sql("select jr_pj from jrn where jr_grpt_id=$seq");
if (pg_num_rows($ret) != 0) {
$r = pg_fetch_array($ret, 0);
$old_oid = $r['jr_pj'];
if (strlen($old_oid??"") != 0)
$this->lo_unlink( $old_oid);
}
// Load new document
$this->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2,
jr_pj_type=$3 where jr_grpt_id=$4",
array($oid, $_FILES['pj']['name'], $_FILES['pj']['type'], $seq));
return $oid;
}
/**
* \brief Get version of a database, the content of the

View file

@ -19,10 +19,14 @@
*/
// Copyright Author Dany De Bontridder danydb@aevalys.eu
/*! \file
/*!
* \file
* \brief Class Document corresponds to the table document
*/
/*! \brief Class Document corresponds to the table document
/*!
* \class
* \brief
* Class Document corresponds to the table DOCUMENT
*/
class Document
@ -38,7 +42,7 @@ class Document
var $d_number; /*!< $d_number number of the document */
var $md_id; /*!< $md_id document's template */
var $f_id; /*!< fiche.f_id */
private $counter; /*!< counter for the items ( goods ) */
protected $counter; /*!< counter for the items ( goods ) */
var $d_name; /*!< document name */
var $md_type ; /*!< Type of document */
/*!
@ -54,6 +58,24 @@ class Document
// counter for MARCH_NEXT
$this->counter=0;
}
function __toString(): string
{
return "Document[db=" . $this->db
. ", d_id=" . $this->d_id
. ", ag_id=" . $this->ag_id
. ", d_mimetype=" . $this->d_mimetype
. ", d_filename=" . $this->d_filename
. ", d_lob=" . $this->d_lob
. ", d_description=" . $this->d_description
. ", d_number=" . $this->d_number
. ", md_id=" . $this->md_id
. ", f_id=" . $this->f_id
. ", counter=" . $this->counter
. ", d_name=" . $this->d_name
. ", md_type=" . $this->md_type
. "]";
}
/**
* @brief insert a minimal document and set the d_id
@ -105,16 +127,20 @@ class Document
/*!
* \brief Generate the document, Call $this-\>replace to replace
* tag by value
* @param p_array contains the data normally it is the $_POST
* @param $p_filename contains the new filename
* @param p_array contains the data normally it is the $_POST (see Acc_Ledger_Sale or
* Acc_Ledger_Purchase)
* @see Acc_Ledger_Sale
* @see Acc_Ledger_Purchase
* @param $p_filename contains the new filename, if not given the filename will be generated
* \return an string : the url where the generated doc can be found, the name
* of the file and his mimetype
*/
function generate($p_array, $p_filename="")
{
try {
// create a temp directory in /tmp to unpack file and to parse it
///@var $dirname (string) temp directory in /tmp to unpack file and to parse it
$dirname=tempnam($_ENV['TMP'], 'doc_');
if ($dirname == false) {
throw new Exception ('DC117 cannot create tmp file',5000);
@ -124,6 +150,31 @@ class Document
if ( mkdir($dirname) == false ) {
throw new Exception ("DC121 cannot create $dirname directory",5000);
}
/**
* md_id == -2 is the standard PDF invoice, you don't parse or compute
* it
*/
if ( $this->md_id == -2)
{
$file_to_parse=str_replace(
array('/', '*', '<', '>', ';', ',', '\\', '.', ':', '(', ')', ' ', '[', ']')
, "-"
, "inv-std-".$p_array['e_pj'].".pdf");
$this->d_number=$this->db->get_next_seq("seq_doc_type_stdinv");
$this->d_filename=$file_to_parse;
$this->d_mimetype="application/pdf";
$this->d_name=$file_to_parse;
$standard_invoice=new \Noalyss\Invoice_PDF($this->db,$dirname,$file_to_parse);
$standard_invoice->set_data($p_array);
$standard_invoice->export();
$this->saveGenerated($dirname.DIRECTORY_SEPARATOR.$file_to_parse);
// Invoice
$href=http_build_query(array('gDossier'=>Dossier::id(), "d_id"=>$this->d_id, 'act'=>'RAW:document'));
$ret='<A class="mtitle" HREF="export.php?'.$href.'">'._('Document').'</A>';
return $ret;
}
// Retrieve the lob and save it into $dirname
$this->db->start();
$dm_info="select md_name,md_type,md_lob,md_filename,md_mimetype
@ -145,7 +196,8 @@ class Document
record_log(sprintf('DOCUMENT.GENERATE.D1 , export failed %s %s',$dirname, $filename));
throw new Exception(sprintf(_("Export a échoué pour %s"), $filename));
}
// $type (letter) type of document : OOo for openoffice otherwise n , with OOo the file
// is a ZIP XML
$type="n";
// if the doc is a OOo, we need to unzip it first
// and the name of the file to change is always content.xml
@ -247,7 +299,7 @@ class Document
{
if (mkdir($temp_dir)==false)
{
$msg=sprintf("D221."._("Ne peut pas créer le répertoire %s", $temp_dir));
$msg=sprintf("D221."._("Ne peut pas créer le répertoire %s"), $temp_dir);
record_log("D221".$msg);
throw new Exception($msg);
}
@ -332,10 +384,9 @@ class Document
}
/*!
* \brief Save the generated Document
* \brief insert the generated Document into the database, update the $this->d_id
* that is the PK of document. and load the PDF into the database.
* \param $p_file is the generated file
*
*
* \return 0 if no error otherwise 1
*/
@ -716,22 +767,22 @@ class Document
$p_tag=noalyss_str_replace('=', '', $p_tag);
$r="Tag inconnu";
static $aComment=NULL;
static $counter_comment=1; /* <! counter for the comment , skip the first one which is the descrition */
static $counter_comment=1; /*<! counter for the comment , skip the first one which is the descrition */
static $aRelatedAction=NULL;
static $counter_related_action=0; /* <! counter for the related action */
static $counter_related_action=0; /*<! counter for the related action */
static $aRelatedOperation=NULL;
static $counter_related_operation=0; /* <! counter for the related operation */
static $counter_related_operation=0; /*<! counter for the related operation */
static $aFileAttached=NULL;
static $counter_file=0; /* <! counter for the file */
static $counter_file=0; /*<! counter for the file */
static $aOtherCard=NULL;
static $counter_other_card=0; /* <! counter for the other card */
static $counter_other_card=0; /*<! counter for the other card */
static $aTag=NULL;
static $counter_tag=0; /* <! counter for the tags */
static $counter_tag=0; /*<! counter for the tags */
static $aParameterExtra=NULL; // Extra parameter for the company
switch ($p_tag)
@ -1769,14 +1820,11 @@ class Document
}
/*!
* \brief Move a document from the table document into the concerned row
* \brief Move a document from the table document into the concerned operation
* the document is not copied : it is only a link
*
* \param $p_internal internal code
*
*/
function moveDocumentPj($p_internal)
function moveDocumentACC($p_internal)
{
$sql="update jrn set jr_pj=$1,jr_pj_name=$2,jr_pj_type=$3 where jr_internal=$4";
@ -1836,7 +1884,7 @@ class Document
}
/**
* replace a pattern with a value in the buffer , handle the change for OOo type file and amount
* @brief replace a pattern with a value in the buffer , handle the change for OOo type file and amount
*
* @param string $p_buffer
* @param string $_pattern
@ -1890,14 +1938,14 @@ class Document
function export_file($p_destination_file)
{
if ($this->d_id==0) {
return;
return false;
}
$this->db->start();
$ret=$this->db->exec_sql(
"select d_id,d_lob,d_filename,d_mimetype from document where d_id=$1", [$this->d_id]);
if (Database::num_row($ret)==0)
{
return;
return false;
}
$row=Database::fetch_array($ret, 0);
//the document is saved into file $tmp
@ -1916,15 +1964,14 @@ class Document
}
/**
* @brief transform the current Document to a PDF, returns the full path of the PDF from the TMP folder
* if the file IS a pdf , then export it and return the path to the file.
*
* @todo replace use of unoconv with a PHP lib to convert into PDF
* @return string full path to the PDF file
*/
function transform2pdf()
{
if (GENERATE_PDF == 'NO' ) {
\record_log(__FILE__."D1857 PDF not available");
throw new \Exception("Cannot not transform to PDF",5000);
}
// Extract from public.document
// Extract from public.document
$dirname=tempnam($_ENV['TMP'],"document");
if ( $dirname == false ) {
@ -1935,6 +1982,16 @@ class Document
if ( mkdir($dirname) == false ) {
throw new Exception("D1868.cannot create tmp directory",5000);
}
if ( $this->d_mimetype == "application/pdf") {
$destination_file=$dirname."/".$this->d_filename;
$this->export_file($destination_file);
return $dirname."/".$destination_file;
return;
}
if (GENERATE_PDF == 'NO' ) {
\record_log(__FILE__."D1857 PDF not available");
throw new \Exception("Cannot not transform to PDF",5000);
}
$destination_file=$dirname."/".$this->d_filename;
$this->export_file($destination_file);

View file

@ -329,7 +329,7 @@ class Fiche
* @param int $p_ad_id AD_ID from attr_def.ad_id
* @param int $p_return 1 return NOTFOUND otherwise an empty string
* @see constant.php
* @return string
* @return string
* @note reread data from database and so it reset previous unsaved change
*/
function get_attribute($p_ad_id,$p_return=1)
@ -1345,16 +1345,17 @@ class Fiche
bcscale(4);
$gDossier=dossier::id();
$p_search=sql_string($p_search);
$script=$_SERVER['PHP_SELF'];
$script=$_SERVER['PHP_SELF']??"";
// Creation of the nav bar
// Get the max numberRow
$filter_amount='';
global $g_user;
$filter_year=" j_tech_per in (select p_id from parm_periode ".
"where p_exercice='".$g_user->get_exercice()."')";
if ( $p_amount) $filter_amount=' and f_id in (select f_id from jrnx where '.$filter_year.')';
if ($p_amount) {
$filter_amount = ' and f_id in (select f_id from jrnx where ' . $filter_year . ')';
}
$all_tiers=$this->count_by_modele($this->fiche_def_ref,"",$p_sql.$filter_amount);
// Get offset and page variable
@ -1495,21 +1496,23 @@ class Fiche
return $this->fiche_def;
}
/*!
***************************************************
* \brief Check if a fiche is used by a jrn
* \brief Check if a card can be used and then belong tp a specific ledger, it the card
*
* return 1 if the fiche is in the range otherwise 0, the quick_code
* or the id must be set
*
*
* \param $p_jrn journal_id
* \param $p_type : deb or cred default empty
* \param $jrn_def_id journal_id (JRN.JRN_DEF_ID)
* \param $side : deb or cred , default empty = both
*
* \return 1 if the fiche is in the range otherwise < 1
* -1 the card doesn't exist
* -2 the ledger has no card to check
* \return 1 if the card belongs to the ledger,
* 0 the card doesn't belong,
* -1 the card doesn't exist,
* -2 the ledger has no card to check,
* -3 there is no category of card for this ledger
*
*/
function belong_ledger($p_jrn,$p_type="")
function belong_ledger($jrn_def_id,$side="")
{
// check if we have a quick_code or a f_id
if (($this->quick_code==null || $this->quick_code == "" )
@ -1519,41 +1522,38 @@ class Fiche
}
//retrieve the quick_code
if ( $this->quick_code=="")
$this->quick_code=$this->get_quick_code();
if ( $this->quick_code==null)
return -1;
if ( $this->id == 0 )
if ( $this->get_by_qcode(null,false) == 1)
return -1;
$get="";
if ( $p_type == 'deb' )
{
$get='jrn_def_fiche_deb';
}elseif ( $p_type == 'cred' )
{
$get='jrn_def_fiche_cred';
if ($this->quick_code == "") {
$this->quick_code = $this->get_quick_code();
}
if ( $get != "" )
if ($this->quick_code == null) {
return -1;
}
if ($this->id == 0 && $this->get_by_qcode(null, false) == 1) {
return -1;
}
if ( $side == 'deb' )
{
$Res=$this->cn->exec_sql("select $get as fiche from jrn_def where jrn_def_id=$p_jrn");
$Res=$this->cn->exec_sql("select jrn_def_fiche_deb as fiche from jrn_def where jrn_def_id=$1",[$jrn_def_id]);
}elseif ( $side == 'cred' )
{
$Res=$this->cn->exec_sql("select jrn_def_fiche_cred as fiche from jrn_def where jrn_def_id=$1",[$jrn_def_id]);
}
else
{
// Get all the fiche type (deb and cred)
$Res=$this->cn->exec_sql(" select jrn_def_fiche_cred as fiche
from jrn_def where jrn_def_id=$p_jrn
from jrn_def where jrn_def_id=$1
union
select jrn_def_fiche_deb
from jrn_def where jrn_def_id=$p_jrn"
from jrn_def where jrn_def_id=$1",
[$jrn_def_id]
);
}
$Max=Database::num_row($Res);
if ( $Max==0)
if ( Database::num_row($Res)==0)
{
return -2;
}
@ -1582,11 +1582,11 @@ class Fiche
fd_id in (".$str_list.") and f_id= ".$this->id;
$Res=$this->cn->exec_sql($sql);
$Max=Database::num_row($Res);
if ($Max==0 )
if (Database::num_row($Res) == 0) {
return 0;
else
return 1;
}
return 1;
}
/*!
* \brief get all the card from a categorie
@ -1968,12 +1968,12 @@ class Fiche
}
/**
* @brief create a card from a qcode and returns a card
* @param string $p_qcode qcode of the card
* @param $cn Database cnx
* @param $p_qcode (string) qcode of the card
*/
static function from_qcode($p_qcode)
static function from_qcode(Database $cn,string $p_qcode)
{
$cn=Dossier::connect();
$card=new Card($cn);
$card=new Fiche($cn);
$card->get_by_qcode($p_qcode);
return $card;
}

View file

@ -0,0 +1,332 @@
<?php
namespace Noalyss;
/*
* This file is part of NOALYSS.
*
* NOALYSS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* NOALYSS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NOALYSS; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Copyright Author Dany De Bontridder danydb@aevalys.eu 22/10/23
/**
* @file
* @brief create a standard invoice
*/
/**
* @class Invoice PDF
* @brief create a standard invoice
*/
class Invoice_PDF extends \PDF
{
private $data; //!< $data (array) see Acc_Ledger_Purchases
function __construct(
\Database $cn
, private $dirname //!< folder where to save file
, private $filename //!< filename to use
)
{
parent::__construct($cn);
}
public function get_dirname()
{
return $this->dirname;
}
public function get_filename()
{
return $this->filename;
}
public function set_dirname($dirname)
{
$this->dirname = $dirname;
return $this;
}
public function set_filename($filename)
{
$this->filename = $filename;
return $this;
}
function set_data($array)
{
$this->data = $array;
return $this;
}
function get_data()
{
return $this->data;
}
function footer()
{
//Position at 1 cm from bottom
$this->SetY(-10);
//Arial italic 8
$this->SetFont('Arial', '', 8);
//Page number
parent::Cell(0, 8, " Page " . $this->PageNo() . '/{nb}', 0, 0, 'C');
parent::Ln(3);
}
function header()
{
global $g_parameter;
$this->setY(15);
$this->SetFont('DejaVu', '', 6);
$colsize = 90;
$this->write_multi($colsize, 3, $g_parameter->MY_NAME);
$this->write_multi($colsize, 3, $this->data['e_date'], border: '', align: 'R');
$this->line_new();
$this->write_multi($colsize, 3,
sprintf("%s %s "
, $g_parameter->MY_STREET
, $g_parameter->MY_NUMBER));
$this->line_new();
$this->write_multi($colsize, 3, $g_parameter->MY_POSTCODE
. " " . $g_parameter->MY_CITY
. " " . $g_parameter->MY_COUNTRY
);
$this->line_new();
$this->write_multi($colsize, 3, $g_parameter->MY_TVA);
$this->line_new();
$email_company = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['INVOICE_EMAIL_COMPANY']);
$site = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['WEB_COMPANY']);
// for FRANCE , the SIREN and SIRET must be given
$siren = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['SIREN']);
$siret = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['SIRET']);
$iban = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['COMPANY_BANK_IBAN']);
$bic = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['COMPANY_BANK_BIC']);
if ($siret != "")
{
$this->write_multi($colsize, 3, "SIRET $siret");
$this->line_new();
}
if ($siren != "")
{
$this->write_multi($colsize, 3, "SIREN $siren");
$this->line_new();
}
if ($iban != "")
{
$this->write_multi($colsize, 3, "IBAN $iban BIC $bic");
$this->line_new();
}
if ($g_parameter->MY_PHONE != "")
{
$this->write_multi($colsize, 3, sprintf(_("Tel %s "),
$g_parameter->MY_PHONE
));
$this->line_new();
}
if ($email_company != "")
{
$this->write_multi($colsize, 3, sprintf(_("email %s "),
$email_company));
$this->line_new();
}
if ($site != "")
{
$this->write_multi($colsize, 3, sprintf(_("site %s"),
$site
));
$this->line_new();
}
$this->setFont("DejaVu", 'B', 14);
$this->write_multi(40, 10, "");
$this->write_multi(100, 10, _("Facture") . " " . $this->data['e_pj'], border: 1, align: 'C');
$this->write_multi(40, 10, "");
$this->line_new(10);
$this->ln(5);
}
//!
//@brief make the invoice
function export()
{
$this->SetAuthor('NOALYSS');
$this->AliasNbPages();
$this->AddPage();
$this->SetAutoPageBreak(true, $this->bMargin*1);
$this->setTitle($this->filename, true);
// $customer (Fiche) retrieve card of the customer
$customer = new \Fiche($this->cn);
$customer->get_by_qcode(trim($this->data['e_client']));
$this->setFont("DejaVu", '', 7);
$this->write_cell(50, 4, "");
$this->write_cell(60, 4, sprintf(_("Echéance %s"), $this->data['e_ech']));
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(100, 4, _("Client"), 'B', 'R');
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(110, 4, $customer->get_attribute(ATTR_DEF_NAME)
. " " . $customer->get_attribute(ATTR_DEF_FIRST_NAME, 0));
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(110, 4, $customer->get_attribute(ATTR_DEF_ADRESS, 0));
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(110, 4, $customer->get_attribute(ATTR_DEF_POSTCODE, 0)
. " " . $customer->get_attribute(ATTR_DEF_CITY, 0)
);
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(110, 4, $customer->get_attribute(ATTR_DEF_COUNTRY, 0));
$this->line_new();
$this->write_cell(50, 4, "");
$this->write_cell(110, 4, $customer->get_attribute(ATTR_DEF_NUMTVA, 0));
$this->line_new();
$a_tva_amount = [];
$a_tva_code = [];
$col = array(
"quick_code" => 30,
"label" => 80,
"quantity" => 25,
"price" => 25,
"vat_code" => 20
);
$this->SetFont("DejaVu", "B", 12);
$this->write_multi(50, 20, "");
$this->write_multi(50, 20, _("Détails"));
$this->line_new();
$this->SetFont("DejaVuCond", "", 7);
$currency = new \Acc_Currency($this->cn, $this->data['p_currency_code']);
$this->write_multi(60, 4, sprintf(_("Les montants sont en %s taux %s")
, $currency->get_code()
, $this->data['p_currency_rate']));
$this->line_new(4);
if ($this->data["bon_comm"] != "")
{
$this->write_multi(120, 4, sprintf(_("Bon de commande / référence %s")
, $this->data["bon_comm"]));
$this->line_new(4);
}
$this->line_new(4);
$this->SetFont("DejaVu", "", 7);
$this->write_multi($col['quick_code'], 4, _("Article"), 1);
$this->write_multi($col['label'], 4, _("Description"), 1);
$this->write_multi($col['quantity'], 4, _("Quantité"), 1);
$this->write_multi($col['price'], 4, _("Prix"), 1);
$this->write_multi($col['vat_code'], 4, _("TVA"), 1);
$this->line_new();
///@var $tot_amount (float) total amount without VAT
///@var $tot_vat (float) total VAT
///@var $line (int) line printed
$tot_amount = $tot_vat = $line =0;
for ($i = 0; $i < $this->data['nb_item']; $i++)
{
$item = new \Fiche($this->cn);
if (!isset($this->data['e_march' . $i]) || $this->data['e_march' . $i] == "")
{
continue;
}
$line++;
$item->get_by_qcode(trim($this->data['e_march' . $i]));
$fill = $this->is_fill($line);
$this->write_multi($col['quick_code'], 4, $item->get_attribute(ATTR_DEF_QUICKCODE),'','',$fill);
$this->write_multi($col['label'], 4, $this->data['e_march' . $i . '_label'],fill:$fill);
$this->write_multi($col['quantity'], 4, nbm($this->data['e_quant' . $i]), '', 'R',fill:$fill);
$this->write_multi($col['price'], 4, nbm($this->data['e_march' . $i . '_price']), '', 'R',fill:$fill);
$this->write_multi($col['vat_code'], 4, $this->data['e_march' . $i . '_tva_id'], '', 'C',fill:$fill);
$x = $this->data['e_march' . $i . '_tva_id'];
if (!isset($a_tva_amount[$x]))
{
$a_tva_amount[$x] = 0;
}
$a_tva_amount[$x] = bcadd($a_tva_amount[$x], $this->data["e_march" . $i . "_tva_amount"], 2);
$tot_amount = bcadd($tot_amount
, bcmul($this->data['e_march' . $i . '_price']
, $this->data['e_quant' . $i]
, 2
)
, 2);
$tot_vat = bcadd($tot_vat
, $this->data['e_march' . $i . '_tva_amount']
, 2);
$this->line_new(4);
if ($this->GetY()>250) {
$this->AddPage();
}
}
$this->line_new(10);
$this->SetFont("DejaVu", "B", 9);
$this->write_multi(30, 4, _("TVA"));
$this->line_new(5);
$this->SetFont("DejaVu", "", 7);
foreach ($a_tva_amount as $tva_id => $tva_amount)
{
$tva = \Acc_Tva::build($this->cn, $tva_id);
$this->write_multi(20, 4, "");
$this->write_multi(80, 4, $tva->tva_id
. " / " . $tva->tva_code
. " / " . $tva->tva_label
. " / " . $tva->tva_rate * 100
);
$this->write_multi(50, 4, $tva_amount);
$this->line_new();
}
$this->ln(20);
$this->SetFont("DejaVu", "B", 9);
$this->write_multi(30, 4, _("TOTAUX"));
$this->line_new();
$this->SetFont("DejaVu", "", 7);
$this->write_multi(60, 4, _("Total Hors TVA "));
$this->write_multi(60, 4, nbm($tot_amount), '', 'R');
$this->line_new();
$this->write_multi(60, 4, _("Total TVA "));
$this->write_multi(60, 4, nbm($tot_vat), '', 'R');
$this->line_new();
$this->write_multi(60, 4, _("Total "));
$this->write_multi(60, 4, nbm(bcadd($tot_amount, $tot_vat, 2),2), '', 'R');
$this->line_new();
$iban = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['COMPANY_BANK_IBAN']);
if ($this->data['e_ech'] != "" && $iban != "")
{
$info = ($this->data["other_info"] == "") ? $this->data["e_pj"] : $this->data["other_info"];
$bic = $this->cn->get_value("select pe_value from parameter_extra where pe_code=$1",
['COMPANY_BANK_IBAN']);
$this->write_multi(150, 4,
sprintf(_("Paiement avant le %s sur le compte %s (BIC %s) avec comme message %s"),
$this->data['e_ech']
, $iban
, $bic
, $this->data["other_info"]
)
);
$this->line_new();
}
$this->Output($this->dirname . DIRECTORY_SEPARATOR . $this->filename, "F");
}
}

View file

@ -227,10 +227,10 @@ select sum(signed_amount) delta,sum(debit) debit,sum(credit) credit from saldo_d
$ledger->save($oe_data);
$oe_result=_("Détail opération");
$oe_result.=sprintf('<a class="detail" style="display:inline" href="javascript:modifyOperation(%d,%d)">%s</a><hr>',
$ledger->jr_id, dossier::id(), $ledger->internal);
$ledger->jr_id, dossier::id(), $ledger->jr_internal);
$cn->exec_sql("update operation_exercice set oe_transfer_date=to_timestamp($1,'DD.MM.YY HH24:MI') , jr_internal=$2 where oe_id=$3",
[date('d.m.Y H:i'),$ledger->internal,$this->operation_exercice_sql->oe_id]);
[date('d.m.Y H:i'),$ledger->jr_internal,$this->operation_exercice_sql->oe_id]);
$cn->commit();
return true;

View file

@ -191,13 +191,13 @@ class Tva_Rate_MTable extends Manage_Table_SQL
$text->selected=$value;
$text->transform(array(
null=>_('-')
,"S"=>_('Taux standard')
,'AE'=>_('Autoliquidate mais pas INTRACOMM.')
,'Z'=>_("TVA à 0%")
,'K'=>_('Autoliquidation INTRACOMM.')
,'G'=>_('TVA exempt pour export hors Europe')
,'O'=>_('TVA Hors périmètre application')
,'E'=>_('Exempté de TVA')
,"S"=>_('S Taux standard')
,'AE'=>_('AE Autoliquidate mais pas INTRACOMM.')
,'Z'=>_("Z TVA à 0%")
,'K'=>_('K Autoliquidation INTRACOMM.')
,'G'=>_('G TVA exempt pour export hors Europe')
,'O'=>_('O TVA Hors périmètre application')
,'E'=>_('E Exempté de TVA')
));
echo $text->input();
}elseif ($key == "tva_id") {