<?php
namespace Noalyss\XMLDocument;

//use Noalyss\Utility;
/*
 *   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 mother for e-invoice : ubl2.1 , Factur-X
 */
/**
 * @class XMLInvoice
 * @brief Mother class for e-invoice 
 *      - $data is 
 * * @code
(
    [id] => 25.822
    [issue_date] => 2025-06-10
    [due_date] => 
    [supplier] => Array
        (
            [name] => My company sprl
            [street] => Allée des Zoulons
            [postalzone] => 1080
            [city] => Molenbeek Saint Jean
            [country] => BE
            [supplier_vat_id] => BE012345678
            [registration_name] => My Company sprl
        )

    [customer] => Array
        (
            [name] => This asbl
            [street] => 
            [postalzone] => 
            [city] => 
            [country] => 
            [customer_vat_id] => numéro TVA
            [registration_name] => This asbl
        )

    [currency] => 0
    [info] => Array 
           [order] = order reference
           [communication] = communication added to the invoice
    [operation] => Array
        (
            [0] => Array
                (
                    [card_id] => 568
                    [quantity] => 1.0000
                    [price] => 10.0000
                    [vat] => 2.1000
                    [vat_id] => 1
                    [vat_reversed] => 0.0000
                    [code_quantity]=> EA
                    [vat_code]=> Code VAT for PEPPOL (S,K,...)
                )

            [1] => Array
                (
                    [card_id] => 164
                    [quantity] => 5.0000
                    [price] => 83.4500
                    [vat] => 17.5200
                    [vat_id] => 1
                    [vat_reversed] => 0.0000
                    [code_quantity]=> EA
                    [vat_code]=> Code VAT for PEPPOL (S,K,...)
                )

            [2] => Array
                (
                    [card_id] => 483
                    [quantity] => 1.0000
                    [price] => 72.0000
                    [vat] => 15.1200
                    [vat_id] => 5
                    [vat_reversed] => 15.1200
                    [code_quantity]=> EA
                    [vat_code]=> Code VAT for PEPPOL (S,K,...)
                )

        )
        
 * 
 *      
)
   

     * @endcode
 *  
 */
abstract class XMLInvoice extends \DOMDocument
{
    protected $cn; //!< Database conx , current folder
    protected $data; //! $data (Array) data retrieve from DB
    protected $jr_id; //! $jr_id (int) is JRN.JR_ID
    function __construct(\Database $conx)
    {
        parent::__construct("1.0", "UTF-8");
        $this->cn=$conx;
        $this->data=[];
    }
    /**
     * @brief return data
     * @return array
     */
    public function get_data() {
        return $this->data;
    }
    /**
     * @brief returns data
     * @param $data (array)
     * @return XMLInvoice
     */
    public function set_data($data): XMLInvoice {
        $this->data = $data;
        return $this;
    }

        /**
     * @brief get Database Connexion
     * @param $cn (\Database)
     * @return XMLInvoice
     */

    public function get_db_conx():\Database {
        return $this->cn;
    }
    /**
     * @brief set Database Connexion
     * @param $cn (\Database)
     * @return XMLInvoice
     */
    public function set_db_conx(\Database $cn): XMLInvoice {
        $this->cn = $cn;
        return $this;
    }

    /**
     * @brief transform an operation ($jr_id) into an array, which contains
     * needed information for making an e-invoice
     * @param $jr_id (int) operation JRN.JR_ID
     * @return array with all info7
     
     * 
     */
    function build_data($jr_id):array
    {
        global $g_parameter;
        $this->jr_id=$jr_id;
        $operation = new \Acc_Sold($this->cn,$jr_id);
        
        $operation->get();
        $result=array();
        $result["id"]= $operation->det->jr_pj_number;
        $result["issue_date"]=$operation->det->jr_date;
        $result["due_date"]=($operation->det->jr_ech=="")?$operation->det->jr_date:$operation->det->jr_ech;
        // supplier
        $result['supplier']=$this->fill_supplier();
       
       
        //customer
        $result['customer']=$this->fill_customer($operation->det->array[0]['qs_client']);
        
        // currency 
        $result['currency']=$this->cn->get_value("select cr_code_iso from currency where id=$1"
                ,array($operation->det->currency_id));
        
        // document description
        $result['description']=$operation->det->jr_comment;
        
        // note if any 

        $result['note']= $result['description'];
        $a_note = $this->cn->get_array("select n_id,n_text from jrn_note where jr_id=$1",[$operation->det->jr_id]);
        $nb_note=count($a_note);
        for ($e=0;$e<$nb_note;$e++)
        {
            
            $result['note'].= html_entity_decode($a_note[$e]['n_text']??"");
        }
        // goods and services
        $result['operation']=array();
        
        $nb_operation= count($operation->det->array);
        for ($i=0;$i < $nb_operation;$i++) {
            $result['operation'][$i]['card_id']=$operation->det->array[$i]['qs_fiche'];
            $result['operation'][$i]['quantity']=$operation->det->array[$i]['qs_quantite'];
            $card=new \Fiche($this->cn,$operation->det->array[$i]['qs_fiche']);
            $result['operation'][$i]['qcode']=$card->get_attribute(ATTR_DEF_QUICKCODE);
            $result['operation'][$i]['name']=$card->get_attribute(ATTR_DEF_NAME);
            $result['operation'][$i]['description']=$operation->det->array[$i]['j_text'];
            if ($operation->det->array[$i]['j_text'] == "") 
            {
                $a=$card->get_attribute(9);
                if ( $a != "") {
                  $result['operation'][$i]['description']=$a;
                }else {
                    $result['operation'][$i]['description']=$result['operation'][$i]['name'];
                }
            }
            // get the type of unity, if not found then it will be EA
            $x= $card->get_attribute(ATTR_DEF_QUANTITY_TYPE,0);
            $result['operation'][$i]['code_quantity']=($x===false||$x=="")?"EA":$x;
            
            // $operation->det->currency_id == 0  default currency of the folder
            if ($operation->det->currency_id == 0 ) {
                $result['operation'][$i]['price']=$operation->det->array[$i]['qs_price'];
                $result['operation'][$i]['price_unit']=$operation->det->array[$i]['qs_unit'];
                $result['operation'][$i]['vat']=$operation->det->array[$i]['qs_vat'];
            } else {
                $result['operation'][$i]['price']=$operation->det->array[$i]['oc_amount'];
                $result['operation'][$i]['price_unit']=bcdiv(
                        $operation->det->array[$i]['oc_amount'],
                        $operation->det->array[$i]['qs_quantite'],
                        2);
                $result['operation'][$i]['vat']=$operation->det->array[$i]['oc_vat_amount'];
                
            }
            $result['operation'][$i]['vat_id']=$operation->det->array[$i]['qs_vat_code'];
//            // tva code for PEPPOL
            $x=$this->cn->get_row("select tva_peppol_code,tva_rate from tva_rate where tva_id=$1"
                    ,[ $result['operation'][$i]['vat_id']]);
            $result['operation'][$i]['vat_code']=($x['tva_peppol_code']=="")?"S":$x['tva_peppol_code'];
            $result['operation'][$i]['vat_rate']=$x['tva_rate'];
            
            $result['operation'][$i]['vat_reversed']=$operation->det->array[$i]['qs_vat_sided'];
        }
        //------------------------------------------------
        // retrieve order and comment
        //------------------------------------------------
        $a_row=$this->cn->get_array("select id_type,ji_value from jrn_info where jr_id=$1"
                ,[$jr_id]);
        $nb_row = count($a_row);
        $result['info']=[];
        $result['info']['order']='NA';
        $result['info']['communication']='';
        for($i=0;$i<$nb_row;$i++) {
            switch ($a_row[$i]['id_type']) {
                case 'BON_COMMANDE':
                    $result['info']['order']=$a_row[$i]['ji_value'];
                    break;
                case 'OTHER':
                    $result['info']['communication']=$a_row[$i]['ji_value'];
                    break;
                        
            }
        }
        $result['info']['communication']=($result['info']['communication']=="")?$result['id']:$result['info']['communication'];
        $result['document']=$this->fill_document($jr_id);
        
         /**
         * Compute totals VAT and AMOUNT
         */
        $nb_operation = count($result['operation']);
        
        /// block cac:LegalMonetaryTotal
        $result['LineExtensionAmount']=0;
        $result['TaxExclusiveAmount']=0;
        $result['TaxInclusiveAmount']=0;
        $result['PayableAmount']=0;
        
        // block cac:TaxTotal
        $result['TaxableAmount']=0;
        $result['TaxAmount']=0;
        
        // array for TaxSubtotal
        $VAT_SubTotal=array();
        $idx_subtotal=0;
        bcscale(2);
        // for each operation 
        $VAT_SubTotal=array();
        for ($i=0;$i < $nb_operation;$i++) {
            $acc_tva=\Acc_TVA::build($this->cn,$result['operation'][$i]['vat_id'] );
            $percent = bcmul($acc_tva->tva_rate,100,2);
            $idx=sprintf("%s - %s",$percent,$result['operation'][$i]['vat_code'] );
            // subtotal for VAT
            $n = find_idx($VAT_SubTotal,'idx',$idx);
            if ($n == -1 ) {
                $n=$idx_subtotal;
                $VAT_SubTotal[$idx_subtotal]=array();
                $VAT_SubTotal[$idx_subtotal]['idx']=$idx;
                $VAT_SubTotal[$idx_subtotal]['vat_code']=$result['operation'][$i]['vat_code'] ;
                if ( $acc_tva->tva_both_side == 1 )
                        $VAT_SubTotal[$idx_subtotal]['percent']=0;
                else
                        $VAT_SubTotal[$idx_subtotal]['percent']=$percent;
                $VAT_SubTotal[$idx_subtotal]['vatex']=$acc_tva->vx_code;
                $VAT_SubTotal[$idx_subtotal]['amount']=$VAT_SubTotal[$idx_subtotal]['vat']=0;
                
                $idx_subtotal++;
            }
            /**
             * @todo Pour les intracomm , quel taux utilisé ? 0 ou 21%
             */
            $VAT_SubTotal[$n]['amount']=bcadd($VAT_SubTotal[$n]['amount'],$result['operation'][$i]['price']);
            if ( $acc_tva->tva_both_side == 0 )
                $VAT_SubTotal[$n]['vat']=bcadd($VAT_SubTotal[$n]['vat'],$result['operation'][$i]['vat']);
            
            $result['TaxableAmount']=bcadd( $result['TaxableAmount'],$result['operation'][$i]['price']);
            
            // $result['TaxAmount']=bcsub( $result['TaxAmount'],$result['operation'][$i]['vat_reversed']);
            $result['operation'][$i]['vat_percent']=$percent;
            if ( $acc_tva->tva_both_side == 1 ) 
            {
                $result['operation'][$i]['vat_percent']=0;
            }else {
                $result['TaxAmount']=bcadd( $result['TaxAmount'],$result['operation'][$i]['vat']);
            }
        }
        $result['subTotalVAT']=$VAT_SubTotal;
        $result['LineExtensionAmount']= $result['TaxableAmount'];
        $result['TaxExclusiveAmount']= $result['TaxableAmount'];
        $result['TaxInclusiveAmount']=bcadd( $result['TaxableAmount'],$result['TaxAmount']);
        $result['PayableAmount']=bcadd( $result['TaxableAmount'],$result['TaxAmount']);
        
        return $result;
        
    }
    

    /**
     * @brief make an array of parameter_extra where pe_code as key and pe_value
     * as value
     * @return array keys : pe_code,pe_value
     */
    function load_noalyss_parameter()
    {
        $r=$this->cn->get_array('
                select pe_code, pe_value from parameter_extra 
                union all 
                select pr_id,pr_value 
                from parameter');
        
        return array_column($r,"pe_value","pe_code");
    }
    /**
     * @brief create an XML invoice based on JRN.JR_ID operation.
     * The PDF file could be added afterward.
     * @parameter $jr_id (int) operation JRN.JR_ID operation
     */
    abstract function make_xml($jr_id);
    /**
     * @brief check that mandatory info are saved in the DB for company (seller)
     */
    abstract function check_company_data() ;
    /**
     * @brief check that mandatory info are saved in the DB for customer
     */
    abstract function check_customer_data() ;
    /**
     * @brief create the invoice in the right format, with PDF if any
     * @param $operation_id (int) JRN.JR_ID
     * @return string : XML or PDF format
     */
    abstract function create_invoice($operation_id) ;
    
    /**
     * @brief display_error display a warning with all error
     */
    public function display_error()
    {
        $a_error=$this->verify();
        include NOALYSS_TEMPLATE."/xmlinvoice-display_error.php";
        
    }
    /**
     * @brief check that the VAT is using a PEPPOL Code
     */
    abstract function check_VAT();

    /**
     * @brief thanks MY_INVOICE_FORMAT , create the corresponding object  
     *      - UBL21BEL => InvoiceUBL21
     *      - FacturX => FACTURXFR
     * @returns null  MY_INVOICE_FORMAT is BASIC
     */
    static function build_xmlinvoice(\Database $conx) {
        global $g_parameter;
        if ($g_parameter->MY_INVOICE_FORMAT == 'UBL21BEL') {
            return new \Noalyss\XMLDocument\InvoiceUBL21($conx);
        }
        if ($g_parameter->MY_INVOICE_FORMAT == 'FACTURXFR') {
            return new \Noalyss\XMLDocument\FacturX($conx);
        }
        return null;
    }
    
    /**
     * @brief check that all the data are correct
     * @returns empty arry : no errors,  array with error code
     * @see get_message_error
     */
    public function verify()
    {
                // verify all VAT
        ///@var $a_error : array of error_code see check_company_error
        $a_error = array();
        $a_error['general'] =  [];
        $a_error['operation']=[];
       
        // verify that all needed data in PARAMETER are valid
        $a_error['company'] = $this->check_company_data();
        $a_error['customer'] = $this->check_customer_data();
        
        return $a_error;
    } 
     
    /**
     * @brief  retrieve data from customer and return it into an array
     * @param $card_id (int) FICHE.F_ID
     * @return array keys : 
     *      - name
     *      - ,street
     *      - ,postalzone
     *      - ,city
     *      - ,country
     *      - ,customer_vat_id => VAT Number
     *      - , registration_name,
     *      - card_id
     *      - endpoint_id
     * @note :        
     * endpoint_id: 
          normally it this the VAT number (BE included) and scheme 9925
          or the scheme 00208 VAT number without BE (enterprise number)

     */
    function fill_customer($card_id):array
    {
        $customer =new \Fiche($this->cn,$card_id);
        $result=array();
        $result['card_id']=$card_id;
        $result['name']=$customer->get_attribute(ATTR_DEF_NAME,0);
        $result['street']=$customer->get_attribute(ATTR_DEF_ADRESS,0);
        $result['postalzone']=$customer->get_attribute(ATTR_DEF_POSTCODE,0);
        $result['city']=$customer->get_attribute(ATTR_DEF_CITY,0);
        
        
        // official ID , like VAT
        $result['customer_vat_id']=str_replace([" ",".","-","/"],"" ,$customer->get_attribute(ATTR_DEF_NUMTVA,0));
        // official name of the company 
        $result['registration_name']=$customer->get_attribute(ATTR_DEF_NAME,0);
        // find country_code of this card
        $result['country']=$customer->get_attribute(ATTR_DEF_COUNTRY_CODE,0);
        if ( $result['country'] == "")
        {
           $result['country']=substr($result['customer_vat_id'],0,2);
        }
        $result['endpoint_id']=$customer->get_attribute(ATTR_DEF_PEPPOLID,0);
        
        
        return $result;
    }
    /**
     * @brief complete $this->data from $g_parameter (global variable) for 
     * Noalyss_Folder_Parameter
     * @return array keys : 
     *      - name
     *      - ,street
     *      - ,postalzone
     *      - ,city
     *      - ,country
     *      - supplier_vat_id => VAT Number avec BE !
     *      - registration_name,
     * 
     */
    function fill_supplier():array
    {
        $a_parameter=$this->load_noalyss_parameter();
        $result=array();
        $result['name']=$a_parameter['MY_NAME'];
        $result['street']=$a_parameter['MY_STREET'];
        $result['postalzone']=$a_parameter['MY_POSTCODE'];
        $result['city']=$a_parameter['MY_CITY'];
        $result['country']=$a_parameter['MY_COUNTRY'];
        // official name of the company 
        $result['registration_name']=$a_parameter['MY_NAME'];
        // official ID , like VAT
        $result['supplier_vat_id']=str_replace([" ",".","-","/"],"" ,$a_parameter['MY_TVA']);
        /**
         * @TODO vérifier qu'il contient bien BE
         */
        
        $result['COUNTRY_CODE']=$a_parameter['MY_COUNTRY_CODE']?? substr($result['supplier_vat_id'], 0, 2);
        $result['COMPANY_LEGAL_REGISTRATION']=$a_parameter['COMPANY_LEGAL_REGISTRATION']??"";
        $result['COMPANY_LEGAL_ENTITY']=$a_parameter['COMPANY_LEGAL_ENTITY']??"";
        $result['INVOICE_CONTACT_NAME']=$a_parameter['INVOICE_CONTACT_NAME']??"";
        $result['INVOICE_EMAIL_COMPANY']=$a_parameter['INVOICE_EMAIL_COMPANY']??"";
        $result['COMPANY_PEPPOL_ID']=$a_parameter['COMPANY_PEPPOL_ID']??"";
        return $result;
    }
    /**
     * @brief build operation from array 
     * key : 
     *      - [e_march0] =>  Quick code of the item
            - [e_march0_label] => Label of item
            - [e_march0_price] => Unit Price
            - [e_quant0] => Quantity
            - [htva_march0] => Price w/0 VAT
            - [e_march0_tva_id] => Code VAT
            - [e_march0_tva_amount] => Amount VAT 
            - [tva_march0] => Amount VAT (duplicate -> to remove)
            - [tvac_march0] => Total Amount Tax included
     * @param type $a_array
     * @return type
     */
    function fill_operation_from_array($a_array)
    {
        $result=array();
        $http=new \HttpInput();
        $http->set_array($a_array);
        
        $nb_item=$http->get_value("nb_item");
        for ($i=0;$i<$nb_item;$i++)
        {
           if ( $http->get_value("e_march{$i}_tva_id") == "") 
           {
               continue;
           }
           $operation=array();
           $card=\Fiche::from_qcode($this->cn,trim($http->get_value("e_march{$i}")));
           $operation['card_id']=$card->id;
           $operation['quantity']=$http->get_value("e_quant{$i}");
           $operation['price']=$http->get_value("e_march{$i}_price");
           $operation['vat']=$http->get_value("tvac_march{$i}");
           $tva= \Acc_Tva::build($this->cn, $http->get_value("e_march{$i}_tva_id"));
           $operation['vat_id']=$tva->tva_id;
           $operation['vat_reversed']=($tva->tva_both_side==1)?$operation['vat']:0;
           $operation['vat_code']=$tva->tva_peppol_code;
           
           $operation['code_quantity']=$card->get_attribute(ATTR_DEF_QUANTITY_TYPE,0);
           $operation['code_quantity']=($operation['code_quantity']=="")?"EA":$operation['code_quantity'];
           $result[$i]=$operation;
        }
        return $result;
    }
    
    /**
     * @brief set the PDF 
     * @param $pdf_filename (string) full path to the PDF
     * @return $this
     * @throws \Exception if the filename doesn't exist
     */
    public function set_pdf_filename($pdf_filename) {
        if ( !file_exists($pdf_filename)) {
            throw new \Exception("AD65 $pdf_filename doesn't not exist");
        }
        $this->pdf_filename = $pdf_filename;
        return $this;
    }
    /**
     * @brief retrieve additionnal documents but only PDF , not other files
     * @param $jr_id (int) JRN.JR_DEF_ID
     * @return array : empty or keys= (filename,description, log id) from table from JRN_SUP_DOCUMENT
     */
    public function fill_document($jr_id)
    {
        $result=[];
        $a_document=$this->cn->get_array("select js_id,js_filename,js_description,js_lob from jrn_sup_document where js_mimetype='application/pdf' AND jr_id=$1",
                [$jr_id]);
        if ($a_document == null) return array();
        
        foreach ($a_document as $document)
        {
            $result[]=array("filename"=>$document['js_filename']
                ,"description"=>$document['js_description']
                ,'oid'=>$document['js_lob']
                ,'id'=>$document['js_id']
                );
        }
        return $result;
           
    }

}
