<?php
    /**
     *  wsInvoiceSFJ_v40_CA.PHP
     * 
     *  Autor: Felipe Faccinetto
     *  Fecha: Marzo/2022
     * 
     *  DESCRIPCIÓN:
     * 
     *  Valida, crea y timbra un CFDI . 
     * 
     *  PARAMETROS:
     * 
     *  pDatPAC         Cadena codificada en base64 que contiene un arreglo JSON con los datos del PAC
     *  pDatGen         Cadena codificada en base64 que contiene un arreglo JSON con los datos generales del CFDI
     *  pDatEmi         Cadena codificada en base64 que contiene un arreglo JSON con los datos del emisor del CFDI
     *  pDatRec         Cadena codificada en base64 que contiene un arreglo JSON con los datos del receptor del CFDI
     *  pDatArt         Cadena codificada en base64 que contiene un arreglo JSON con los datos de los articulos del CFDI
	  *  pRecordId		   Identidicador del registro a actualizar en Zoho Creator
	  *  pFolioNum		   Numeor de Folio asignado al CFDI por la serie
	  *  pFormatoPDF		Nombre del formato que se utilizara para generar el PDF
     *  pDatRel         Cadena codificada en base64 que contiene un arreglo JSON con los datos de los CFDI Relacionados
     *  pAppOwner       Especifica el propietario de la aplicación que se actualizara.
     *  pInclAdde       Especifica si el CFDI llevara una Addenda
     * 
     * 
    */

    header('Content-Type: text/html; charset=UTF-8');
    include("qrlib/qrlib.php");
    require('oauth.php');

    $bDatPAC  	= "";
    $bDatGen  	= "";
    $bDatEmi  	= "";
    $bDatRec  	= "";
    $bDatArt  	= "";
    $bDatRel 	= "";
	$bRecId   	= "";
	$bFolioN  	= "";
    $bdDatRel   = "";
	$tipoTest 	= 0;
	$FormatoPDF = "wsInvoicePDF_v33";
	$applnkname = 'cfdi'; 
    $TipPDFGen  = 0;
    $appOwner   = "";
    $incAddenda = 0;

    ## Cargamos los parametros enviados al WebService
    if (isset($_REQUEST['pDatPAC']))
    {
        $bDatPAC = $_REQUEST['pDatPAC'];        // Parametros para la conexión con el PAC
    }
    if (isset($_REQUEST['pDatGen']))
    {
        $bDatGen = $_REQUEST['pDatGen'];        // Datos Generales del CFDI
    }

    if (isset($_REQUEST['pDatEmi']))
    {
        $bDatEmi = $_REQUEST['pDatEmi'];        // Datos del Emisor del CFDI
    }

    if (isset($_REQUEST['pDatRec']))
    {
        $bDatRec = $_REQUEST['pDatRec'];        // Datos del Receptor del CFDI
    }

    if (isset($_REQUEST['pDatArt']))
    {
        $bDatArt = $_REQUEST['pDatArt'];        // Datos de los articulos del CFDI
    }

    if (isset($_REQUEST['pRecordId']))
    {
        $bRecId = $_REQUEST['pRecordId'];        // Identidicador del registro
    }

    if (isset($_REQUEST['pFolioNum']))
    {
        $bFolioN = $_REQUEST['pFolioNum'];        // Identidicador del registro
    }

    if (isset($_REQUEST['tipoTest']))
    {
        $tipoTest = $_REQUEST['tipoTest'];        // Indicador de si despliega resultados parciales
    }
	
    if (isset($_REQUEST['pFormatoPDF']))
    {
        $FormatoPDF = $_REQUEST['pFormatoPDF'];        // Nombre del formato para el PDF
    }

    if (isset($_REQUEST['pDatRel']))
    {
        $bDatRel = $_REQUEST['pDatRel'];     	// Datos de los CFDI ReElacionados
    }

    if (isset($_REQUEST['pAppLnkNam']))
    {
        $applnkname = $_REQUEST['pAppLnkNam'];     	// Identificador de la aplicación en Zoho Creator
    }
	 
    if (isset($_REQUEST['pTipPDFGen']))
    {
        $TipPDFGen = $_REQUEST['pTipPDFGen'];        // Parametros para el tipo de formato
    }

    if (isset($_REQUEST['pAppOwner']))
    {
        $appOwner = $_REQUEST['pAppOwner'];        // Parametros para el propietario de la aplicación
    }

    if (isset($_REQUEST['pInclAdde']))
    {
        $incAddenda = $_REQUEST['pInclAdde'];        // Para conocer si el CFDI incluye addenda o no
    }
    
    if($appOwner == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan el Propoietario de la Aplicación");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bRecId == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan el ID del CFDI a actualizar");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatPAC == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos del PAC");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatGen == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos Generales");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatEmi == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos del Emisor");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatRec == "" or $bDatArt == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos del Receptor");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatArt == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos de los Articulos");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    ### 0. EXTRACCION DE PARAMETROS PARA EL CFDI ######################################################
    #== Primero, extraemos el JSON del string en base 64
    $bdDatPAC = base64_decode($bDatPAC);
    $bdDatGen = base64_decode($bDatGen);
    $bdDatEmi = base64_decode($bDatEmi);
    $bdDatRec = base64_decode($bDatRec);
    $bdDatArt = base64_decode($bDatArt);
	if($bDatRel <> "")
	{
		$bdDatRel = base64_decode($bDatRel);
	}

	if ($tipoTest == 1){
		echo $bdDatPAC;
		echo '<br>';
		echo '<br>';
		echo $bdDatGen;
		echo '<br>';
		echo '<br>';
		echo $bdDatEmi;
		echo '<br>';
		echo '<br>';
		echo $bdDatRec;
		echo '<br>';
		echo '<br>';
		echo $bdDatArt;
		echo '<br>';
		echo '<br>';
		echo $bdDatRel;
		echo '<br>';
		echo '<br>';

        $array = json_encode(json_decode($bdDatRec), JSON_PRETTY_PRINT);
        echo $array;
    
    }

    #== Segundo, decodificamos el JSON a un arreglo
    $abdDatPAC = json_decode($bdDatPAC,true);
    $abdDatGen = json_decode($bdDatGen,true);
    $abdDatEmi = json_decode($bdDatEmi,true);
    $abdDatRec = json_decode($bdDatRec,true);
    $abdDatArt = json_decode($bdDatArt,true);
	if($bDatRel <> "")
	{
		$abdDatRel = json_decode($bdDatRel,true);
	}

	if ($tipoTest == 1){
		echo '<div style="font-size: 10pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'AREGLOS DE DATOS DESPUES DE json_decode';
		echo '</div>';
        echo '<br>';
        print_r($abdDatPAC);
        echo '<br>';
        echo '<br>';
        print_r($abdDatGen);
        echo '<br>';
        echo '<br>';
        print_r($abdDatEmi);
        echo '<br>';
        echo '<br>';
        print_r($abdDatRec);
        echo '<br>';
        echo '<br>';
        print_r($abdDatArt);
        echo '<br>';
        echo '<br>';
    }

    $dirBase = realpath("../");

    ### CÓDIGO FUENTE, FACTURACIÓN ELECTRÓNICA CFDI VERSIÓN 3.2 ACORDE A LOS REQUIRIMIENTOS DEL SAT, ANEXO 20.

    ### 1. CONFIGURACIÓN INICIAL ######################################################

    # 1.1 Configuración de zona horaria
    date_default_timezone_set('America/Mexico_City'); // 

    if($tipoTest > 0){
		# 1.2 Muestra la zona horaria predeterminada del servidor (opcional a mostrar)
		echo '<div style="font-size: 10pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'ZONA HORARIA PREDETERMINADA';
		echo '</div>';
		echo '<div style="font-size: 10pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo date_default_timezone_get();
		echo '</div><br>';
	}


    ### 2. DEFINICIÓN DE CONSTANTES ###################################################
    $SendaPEMS  = "archs_pem/";   
    $SendaCFDI  = "archs_cfdi/";  
    $SendaGRAFS = "archs_graf/";  
    $SendaXSD   = "archs_xsd/";   
    
    
    // 2.1 Datos de acceso del usuario (proporcionados por el PAC).
    if($abdDatPAC["tipoTim"] == 1){
        ## Timbrado en producción
        $urlPAC   = "https://solucionfactible.com/ws/services/Timbrado?wsdl";
    }
    else
    {
        ## Timbrado en pruebas
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
    }
    
    $username = $abdDatPAC["username"];
    $password = $abdDatPAC["password"];

	
    if($tipoTest > 0){
		$username = "testing@solucionfactible.com";
		$password = "timbrado.SF.16672";
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
	}
    
    $valorNod = '';
    $metodoDePago  = "";
    
	if ($tipoTest > 0){
		### MUESTRA LOS DATOS DEL USUARIO QUE ESTÁ TIMBRANDO (OPCIONAL A MOSTRAR) ######
		echo '<div style="font-size: 10pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'DATOS DEL USUARIO QUE ESTÁ TIMBRANDO';
		echo '</div>';
		echo '<div style="font-size: 10pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'USUARIO: <span style="color: #088A29; font-size: 11pt;">'.$username."</span><br>";
		echo 'PASSWORD: <span style="color: #088A29; font-size: 11pt;">'.$password."</span><br>";
		echo 'URL: <span style="color: #088A29; font-size: 11pt;">'.$urlPAC."</span><br>";
		echo '</div><br>';  
	}		
    
    ### 3. DEFINICIÓN DE VARIABLES INICIALES ##########################################

    #----------------------------------------------------------------
        #== Llamado para generar el token para donatello
        #----------------------------------------------------------------
        $curl = curl_init();

        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$appOwner,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'GET',
            CURLOPT_HTTPHEADER => array(
            'Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c'
            ),
        ));

        $response = curl_exec($curl);

        curl_close($curl);

        // Decodificamos la respuesta JSON
        $data = json_decode($response, true);

        // Accedemos al valor de 'access_token de donatello'
        $access_token = $data['access_token'];

    if($incAddenda == 1)
    {
        #== Conexión a Zoho Creator para el detalle del CFDI
        $request_url =  'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_I_query/'.$bRecId;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $request_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $access_token)); 

        $r = curl_exec($ch);

        $array = json_decode($r);

        $folio_AdAs   = $array->data->Folio_AdAs;
        $serie_AdAs   = $array->data->Serie_AdAs;
        $no_Prov_AdAs = $array->data->No_Prov_AdAs;
        $ord_Com_AdAs = $array->data->Ord_Comp_AdAs;
        $tip_Pro_AdAs = $array->data->Tipo_Prov_AdAs;
        $jsonAddenda  = $array->data->JSON_Addenda_ASO;
        $ajsonAddenda = json_decode($jsonAddenda,true);

    }

    ### 4. DESTINATARIOS DE E-MAILS, PERSONAS A QUIENES LES LLEGARÁ DE MANERA ADJUNTA LA FACTURA ELECTRÓNICA EN ARCHIVOS .XML Y .PDF
    $CadenaEmails = $abdDatGen["CadenaEmails"];
    $CadenaEmails = "ffaccinetto@aptus-legal.com";
    
    
    ### 5. DATOS GENERALES DE LA FACTURA ##############################################
    $fact_serie        = $abdDatGen["fact_serie"];
    $fact_folio        = $abdDatGen["fact_folio"];
	
	if ($bFolioN <> ""){
		$fact_folio = $bFolioN;
	}
	
    $NoFac             = $fact_serie.$fact_folio;
    $fact_tipcompr     = $abdDatGen["fact_tipcompr"];
    $fact_exportacion  = $abdDatGen["fact_exportacion"]; 
    $tasa_iva          = $abdDatGen["tasa_iva"];
    $subTotal          = number_format($abdDatGen["subTotal"],2,'.','');
    $descuento         = number_format($abdDatGen["descuento"],2,'.','');
    $IVA               = number_format($abdDatGen["IVA"],2,'.','');
    $total             = number_format($abdDatGen["total"],2,'.','');
    $fecha_fact        = $abdDatGen["fecha_fact"];
    $condicionesDePago = $abdDatGen["condicionesDePago"];
    $formaDePago       = utf8_decode($abdDatGen["formaDePago"]);
    $metodoDePago      = utf8_decode($abdDatGen["metodoDePago"]);
    $TipoCambio        = $abdDatGen["TipoCambio"];
    $LugarExpedicion   = utf8_decode($abdDatGen["LugarExpedicion"]);
    $moneda            = $abdDatGen["moneda"];
	$invoiceID		   = $abdDatGen["invoiceID"];
	$invoiceNumber	   = $abdDatGen["invoiceNumber"];
	$TipoRelacion	   = $abdDatGen["TipoRelacion"];

    if($TipPDFGen == 1)
    {
        $sObservaciones	   = base64_encode(utf8_decode($abdDatGen["Observaciones"]));
        $sOrdenCobro	   = $abdDatGen["OrdenCobro"];
    }

    $subTotal 				   = 0;
    $subTotTraslados           = 0;
    $totalImpuestosRetenidos   = 0; // 5.24 Total de impuestos retenidos.
    $totalImpuestosTrasladados = 0; // 5.25 Total de impuestos trasladados.

	if ($tipoTest > 0){
		### 6. MUESTRA LA ZONA HORARIA PREDETERMINADA DEL SERVIDOR (OPCIONAL A MOSTRAR) ######
		echo '<div style="font-size: 10pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'FECHA Y HORA DE SOLICITUD DE TIMBRADO';
		echo '</div>';
		echo '<div style="font-size: 10pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $fecha_fact; // 6.1 Se muestra solo para consultar y confirmar que sea la correcta.
		echo '</div><br>';
	}
   
    // Calculando subTotal, Impuestos Trasladados y Retenidos.
    for ($i = 0; $i<count($abdDatArt); $i++){
        $subTotal                  = $subTotal + $abdDatArt[$i]["importe"];
        $subTotTraslados           = $subTotTraslados + $abdDatArt[$i]["importe"] - $abdDatArt[$i]["descuento"];
        $totalImpuestosTrasladados = $totalImpuestosTrasladados + $abdDatArt[$i]["ivaImporte"];
        $totalImpuestosRetenidos   = $totalImpuestosRetenidos   + $abdDatArt[$i]["isrImporte"];
    }
	
    // Calculando el SubTotal.
    $subTotal     = number_format($subTotal,2,'.',''); 
    $totalImpuestosTrasladados = number_format($totalImpuestosTrasladados,2,'.',''); 
    $totalImpuestosRetenidos   = number_format($totalImpuestosRetenidos,2,'.',''); 
	
    // Calculando Total.
    $total = $subTotal - $descuento + $totalImpuestosTrasladados - $totalImpuestosRetenidos;
	$total = number_format($total,2,'.','');    
	
	### 6. MUESTRA LA ZONA HORARIA PREDETERMINADA DEL SERVIDOR (OPCIONAL A MOSTRAR) ######
    ### 7. ARRAYS QUE CONTIENEN LOS ARTICULOS QUE FORMAN PARTE DE LA VENTA ############
                            
    ### 8. DATOS GENERALES DEL EMISOR #################################################    
    $emisor_rs       = utf8_decode($abdDatEmi["emisor_rs"]);
    $emisor_rfc      = utf8_decode($abdDatEmi["emisor_rfc"]);
    $emisor_regfis   = utf8_decode($abdDatEmi["emisor_regfis"]);

/*    
    #== Domicilio fiscal =======================================================
    $emisor_cal       = utf8_decode($abdDatEmi["emisor_cal"]);
    $emisor_ne        = utf8_decode($abdDatEmi["emisor_ne"]);
    $emisor_ni        = utf8_decode($abdDatEmi["emisor_ni"]);
    $emisor_col       = utf8_decode($abdDatEmi["emisor_col"]);
	$emisor_localidad = utf8_decode($abdDatEmi["emisor_localidad"]);
    $emisor_delmpio   = utf8_decode($abdDatEmi["emisor_delmpio"]);
    $emisor_pais      = utf8_decode($abdDatEmi["emisor_pais"]);
    $emisor_edo       = utf8_decode($abdDatEmi["emisor_edo"]);
    $emisor_cp        = utf8_decode($abdDatEmi["emisor_cp"]);
    $emisor_referdom  = utf8_decode($abdDatEmi["emisor_referdom"]);
*/

    ### 9. DATOS GENERALES DEL RECEPTOR (CLIENTE) #####################################
    $RFC_Recep    = utf8_decode($abdDatRec["RFC_Recep"]);

    if (strlen($RFC_Recep) == 12)
        {
            $RFC_Recep = " ".$RFC_Recep; 
        }
    else
        {
            $RFC_Recep = $RFC_Recep;
        }

    $receptor_rfc = $RFC_Recep;
    $receptor_rs  = utf8_decode($abdDatRec["receptor_rs"]);
	 $receptor_uso = utf8_decode($abdDatGen["usoCFDI"]);
    $receptor_regfis = utf8_decode($abdDatRec["receptor_regfis"]);
    $receptor_cp     = utf8_decode($abdDatRec["receptor_cp"]);

    if($TipPDFGen == 1)
    {
        $receptor_cal = utf8_decode($abdDatRec["receptor_cal"]);
        $receptor_ne  = utf8_decode($abdDatRec["receptor_ne"]);
        $receptor_ni  = utf8_decode($abdDatRec["receptor_ni"]);
        $receptor_col = utf8_decode($abdDatRec["receptor_col"]);
        $receptos_loc = utf8_decode($abdDatRec["receptor_loc"]);
        $receptor_del = utf8_decode($abdDatRec["receptor_del"]);
        $receptor_edo = utf8_decode($abdDatRec["receptor_edo"]);
        $receptor_pai = utf8_decode($abdDatRec["receptor_pai"]);
        $receptor_cp  = utf8_decode($abdDatRec["receptor_cp"]);

        $sDomicilio = $receptor_cal;
        if($receptor_ne != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_ne;
        } 
        if($receptor_ni != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_ni;
        } 
        if($receptor_col != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_col;
        } 
        if($receptos_loc != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptos_loc;
        } 
        if($receptor_del != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_del;
        } 
        if($receptor_edo != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_edo;
        } 
        if($receptor_pai != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_pai;
        } 
        if($receptor_cp != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_cp;
        } 
        $sDireRecep = base64_encode(utf8_encode($sDomicilio));
    }
    

    ### 10. CREACIÓN Y ALMACENAMIENTO DEL ARCHIVO .XML (CFDI) ANTES DE SER TIMBRADO ###################
    
    #== 10.1 Creación de la variable de tipo DOM, aquí se conforma el XML a timbrar posteriormente.
    $xml = new DOMdocument('1.0', 'UTF-8'); 
    $root = $xml->createElement("cfdi:Comprobante");
    $root = $xml->appendChild($root); 
    
    $cadena_original='||';
    $noatt=  array();
    
    #== 10.2 Se crea e inserta el primer nodo donde se declaran los namespaces ======
    cargaAtt($root, array("xsi:schemaLocation"=>"http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd",
            "xmlns:cfdi"=>"http://www.sat.gob.mx/cfd/4",
            "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance"
        )
    );
    
    #== 10.3 Rutina de integración de nodos =========================================
    if($TipPDFGen == 1)
    {
        if ($appOwner == "arochiylindner.aptus")
        {
            cargaAtt($root, array(
                "Version"=>"4.0", 
                "Serie"=>$fact_serie,
                "Folio"=>$invoiceNumber,
                "Fecha"=>$fecha_fact,
                "FormaPago"=>$formaDePago,
                "NoCertificado"=>$noCertificado,
                "CondicionesDePago"=>$condicionesDePago,
                "SubTotal"=>$subTotal,
                "Descuento"=>$descuento,
                "Moneda"=>$moneda,
                "TipoCambio"=>$TipoCambio,
                "Total"=>$total,
                "TipoDeComprobante"=>$fact_tipcompr,
                "Exportacion"=>$fact_exportacion,
                "MetodoPago"=>$metodoDePago,
                "LugarExpedicion"=>$LugarExpedicion
                )
            );
        }
        else
        {
            cargaAtt($root, array(
                "Version"=>"4.0", 
                "Serie"=>$fact_serie,
                "Folio"=>$invoiceNumber,
                "Fecha"=>date("Y-m-d")."T".date("H:i:s"),
                "FormaPago"=>$formaDePago,
                "NoCertificado"=>$noCertificado,
                "CondicionesDePago"=>$condicionesDePago,
                "SubTotal"=>$subTotal,
                "Descuento"=>$descuento,
                "Moneda"=>$moneda,
                "TipoCambio"=>$TipoCambio,
                "Total"=>$total,
                "TipoDeComprobante"=>$fact_tipcompr,
                "Exportacion"=>$fact_exportacion,
                "MetodoPago"=>$metodoDePago,
                "LugarExpedicion"=>$LugarExpedicion
                )
            );
        }
    }
    else
    {
        cargaAtt($root, array(
                "Version"=>"4.0", 
                "Serie"=>$fact_serie,
                "Folio"=>$fact_folio,
                "Fecha"=>date("Y-m-d")."T".date("H:i:s"),
                "FormaPago"=>$formaDePago,
                "NoCertificado"=>$noCertificado,
                "CondicionesDePago"=>$condicionesDePago,
                "SubTotal"=>$subTotal,
                "Descuento"=>$descuento,
                "Moneda"=>$moneda,
                "TipoCambio"=>$TipoCambio,
                "Total"=>$total,
                "TipoDeComprobante"=>$fact_tipcompr,
                "Exportacion"=>$fact_exportacion,
                "MetodoPago"=>$metodoDePago,
                "LugarExpedicion"=>$LugarExpedicion
            )
        );
    }


	// Adicionado por FFR en Marzo 30, 2019 
	// Para el manejo de los CFDI Relacionados 
	if($bDatRel <> "")
	{
		$cfdiRelacionados = $xml->createElement("cfdi:CfdiRelacionados");
		$cfdiRelacionados = $root->appendChild($cfdiRelacionados);
        cargaAtt($cfdiRelacionados, array("TipoRelacion"=>utf8_decode($TipoRelacion)));
		for ($i=0; $i < count($abdDatRel); $i++)
		{
			$cfdiRelacionado = $xml->createElement("cfdi:CfdiRelacionado");
			$cfdiRelacionado = $cfdiRelacionados->appendChild($cfdiRelacionado);
			cargaAtt($cfdiRelacionado, array("UUID"=>utf8_decode($abdDatRel[$i]["UUID"])));
		}
	}

    $emisor = $xml->createElement("cfdi:Emisor");
    $emisor = $root->appendChild($emisor);
    cargaAtt($emisor, array("Rfc"=>$emisor_rfc,
                            "Nombre"=>$emisor_rs,
                            "RegimenFiscal"=>$emisor_regfis
                             )
                        );
                    
    $receptor = $xml->createElement("cfdi:Receptor");
    $receptor = $root->appendChild($receptor);
    cargaAtt($receptor, array("Rfc"=>$receptor_rfc,
                    "Nombre"=>$receptor_rs,
                    "DomicilioFiscalReceptor"=>$receptor_cp,
                    "RegimenFiscalReceptor"=>$receptor_regfis,
                    "UsoCFDI"=>$receptor_uso
                )
            );

    $conceptos = $xml->createElement("cfdi:Conceptos");
    $conceptos = $root->appendChild($conceptos);
    
	$isrImpuesto = "001";
	$ivaImpuesto = "002";
    
    #== 10.4 Ciclo "for", recopilación de datos de artículos e integración de sus respectivos nodos ==
    $ivaTasaOCuota = 0.160000;
	$isrTasaOCuota = 0.060000;
    for ($i=0; $i<count($abdDatArt); $i++){
       	
        $concepto = $xml->createElement("cfdi:Concepto");
        $concepto = $conceptos->appendChild($concepto);
        cargaAtt($concepto, array(
               "ClaveProdServ"=>utf8_decode($abdDatArt[$i]["claveProd"]),
               "Cantidad"=>$abdDatArt[$i]["cantidad"],
               "ClaveUnidad"=>utf8_decode($abdDatArt[$i]["unidad"]),
               "Unidad"=>trim(utf8_decode($abdDatArt[$i]["descUnidad"])),
               "Descripcion"=>trim(utf8_decode($abdDatArt[$i]["descripcion"])),
               "ValorUnitario"=>number_format($abdDatArt[$i]["valorUnitario"],2,'.',''),
               "Importe"=>number_format($abdDatArt[$i]["importe"],2,'.',''),
               "Descuento"=>number_format($abdDatArt[$i]["descuento"],2,'.',''),
               "ObjetoImp"=>utf8_decode($abdDatArt[$i]["ObjetoImp"])
            )
        );
    
		if ($abdDatArt[$i]["ivaImporte"] > 0 || $abdDatArt[$i]["isrImporte"] > 0)
        //if($abdDatArt[$i]["ivaImpuesto"] == "002" || $abdDatArt[$i]["isrImpuesto"] == "001")
		{
			$impuestos = $xml->createElement("cfdi:Impuestos");
			$impuestos = $concepto->appendChild($impuestos);

			if ($abdDatArt[$i]["ivaImporte"] > 0)
            //if($abdDatArt[$i]["ivaImpuesto"] == "002")
			{
				$Traslados = $xml->createElement("cfdi:Traslados");
				$Traslados = $impuestos->appendChild($Traslados);
				
				$Traslado = $xml->createElement("cfdi:Traslado");
				$Traslado = $Traslados->appendChild($Traslado);
				
				$ivaImpuesto = $abdDatArt[$i]["ivaImpuesto"];
			
				cargaAtt($Traslado, array(
					   "Base"=>number_format($abdDatArt[$i]["ivaBase"],2,'.',''),
					   "Impuesto"=>$abdDatArt[$i]["ivaImpuesto"],
					   "TipoFactor"=>$abdDatArt[$i]["ivaTipoFactor"],
					   "TasaOCuota"=>number_format($abdDatArt[$i]["ivaTasaOCuota"],6,'.',''),
					   "Importe"=>number_format($abdDatArt[$i]["ivaImporte"],2,'.','')
					) 
				);
				$ivaTasaOCuota = $abdDatArt[$i]["ivaTasaOCuota"];
			}
			
			if ($abdDatArt[$i]["isrImporte"] > 0)
            //if($abdDatArt[$i]["isrImpuesto"] == "001")
			{
				$Retenciones = $xml->createElement("cfdi:Retenciones");
				$Retenciones = $impuestos->appendChild($Retenciones);
				
				$Retencion = $xml->createElement("cfdi:Retencion");
				$Retencion = $Retenciones->appendChild($Retencion);
				
				$isrImpuesto = $abdDatArt[$i]["isrImpuesto"];

				cargaAtt($Retencion, array(
					   "Base"=>number_format($abdDatArt[$i]["isrBase"],2,'.',''),
					   "Impuesto"=>$abdDatArt[$i]["isrImpuesto"],
					   "TipoFactor"=>$abdDatArt[$i]["isrTipoFactor"],
					   "TasaOCuota"=>number_format($abdDatArt[$i]["isrTasaOCuota"],6,'.',''),
					   "Importe"=>number_format($abdDatArt[$i]["isrImporte"],2,'.','')
					) 
				);
				$isrTasaOCuota = $abdDatArt[$i]["isrTasaOCuota"];
			}
		}
	}
	
	if ($totalImpuestosTrasladados > 0 || $totalImpuestosRetenidos > 0)
	{
		$Impuestos = $xml->createElement("cfdi:Impuestos");
		$Impuestos = $root->appendChild($Impuestos);

			   
		if ($totalImpuestosRetenidos > 0)
		{
			$Retenciones = $xml->createElement("cfdi:Retenciones");
			$Retenciones = $Impuestos->appendChild($Retenciones);    

				$Retencion = $xml->createElement("cfdi:Retencion");
				$Retencion = $Retenciones->appendChild($Retencion);

					cargaAtt($Retencion, array(
						   "Impuesto"=>$isrImpuesto,
						   "Importe"=>number_format($totalImpuestosRetenidos,2,'.','')
						) 
					);

					cargaAtt($Impuestos, array(
							"TotalImpuestosRetenidos"=>number_format($totalImpuestosRetenidos,2,'.','')
						)
					);
		}

		if ($totalImpuestosTrasladados > 0)
		{
			$Traslados = $xml->createElement("cfdi:Traslados");
			$Traslados = $Impuestos->appendChild($Traslados);

				$Traslado = $xml->createElement("cfdi:Traslado");
				$Traslado = $Traslados->appendChild($Traslado);

					cargaAtt($Traslado, array(
                           "Base"=>number_format($subTotTraslados,2,'.',''), 
						   "Impuesto"=>$ivaImpuesto,
						   "TipoFactor"=>"Tasa",
						   "TasaOCuota"=>number_format($ivaTasaOCuota,6,'.',''),
						   "Importe"=>number_format($totalImpuestosTrasladados,2,'.','')
						) 
					);    
					
					cargaAtt($Impuestos, array(
									"TotalImpuestosTrasladados"=>number_format($totalImpuestosTrasladados,2,'.','')
								)
							);
		}
	}
	
    $complemento = $xml->createElement("cfdi:Complemento");
    $complemento = $root->appendChild($complemento);
    
    #== 10.7 Termina de conformarse la "Cadena original" con doble ||
    $cadena_original .= "|";   
	
	if ($tipoTest > 0){
		#=== Muestra la cadena original (opcional a mostrar) =======================
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'CADENA ORIGINAL';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $cadena_original;
		echo '</div><br>';
	}
    
    #== 10.8 Proceso para obtener el sello digital del archivo .pem.key =========
    $keyid = openssl_get_privatekey(file_get_contents($SendaPEMS.$file_key));
    openssl_sign($cadena_original, $crypttext, $keyid, OPENSSL_ALGO_SHA256);
    openssl_free_key($keyid);

    #== 10.9 Se convierte la cadena digital a Base 64 ===========================
    $sello = base64_encode($crypttext);    
    
	if ($tipoTest > 0){
		#=== Muestra el sello (opcional a mostrar) =================================
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'SELLO';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $sello;
		echo '</div><br>';
	}
    
    #== 10.10 Proceso para extraer el certificado del sello digital ==================
    $file = $SendaPEMS.$file_cer;      // Ruta al archivo
    $datos = file($file);
    $certificado = ""; 
    $carga=false;  
    for ($i=0; $i<sizeof($datos); $i++){
        if (strstr($datos[$i],"END CERTIFICATE")) $carga=false;
        if ($carga) $certificado .= trim($datos[$i]);

        if (strstr($datos[$i],"BEGIN CERTIFICATE")) $carga=true;
    } 
    
	if ($tipoTest > 0){
		#=== Muestra el certificado del sello digital (opcional a mostrar) =========
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'CERTIFICADO DEL SELLO DIGITAL';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $certificado;
		echo '</div><br>';    
	}
    
    
    #== 10.11 Se continua con la integración de nodos ===========================   
    $root->setAttribute("Sello",$sello);
    $root->setAttribute("Certificado",$certificado);   # Certificado.
	
    #== Fin de la integración de nodos =========================================
    
    
    #=== 10.12 Se guarda el archivo .XML antes de ser timbrado =======================
	
    $cfdi = $xml->saveXML();
    $xml->formatOutput = true;             
    $xml->save($SendaCFDI."PreCFDI-33_".$NoFac.".xml"); 
    unset($xml);
    
    #=== 10.13 Se dan permisos de escritura al archivo .xml. =========================
    chmod($SendaCFDI."PreCFDI-33_".$NoFac.".xml", 0777); 
	
    ### 11. PROCESO DE TIMBRADO ########################################################

	if ($tipoTest > 0){
		#=== Se muestra el .XML antes de ser timbrado (opcional a mostrar)==========
		echo '<div style="font-size: 11pt; color: #000099; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'FACTURA .XML A TIMBRAR';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo htmlspecialchars($cfdi);
		echo '</div><br>'; 
	}
    
    #== 11.1 Se crea una variable de tipo DOM y se le carga el CFDI =================================
    $xml2 = new DOMDocument();
    $xml2->loadXML($cfdi); 
    
    #== 11.2 Proceso de validación de la variable de tipo DOM (estructura del CFDI a timbrar) contra el esquema cfdv32.xsd ==========================
   
    $fname = $SendaCFDI."PreCFDI-33_".$NoFac.".xml";
    if(!file_exists($fname)){
     die(PHP_EOL . "File not found" . PHP_EOL . PHP_EOL);
    }
     
    $handle = fopen($fname, "r");
    $sData = '';
    #== 11.3 Convirtiendo el contenido del CFDI a BASE 64 ======================
    while(!feof($handle))
        $sData .= fread($handle, filesize($fname));
    fclose($handle);
    $b64 = base64_encode($sData);
    
    $response = '';

    if($abdDatPAC["tipoTim"] == 1){
        ## Timbrado en producción
        $urlLocation   = 'https://solucionfactible.com/ws/services/Timbrado';
      }
      else
      {
        ## Timbrado en pruebas
        $urlLocation   = 'https://testing.solucionfactible.com/ws/services/Timbrado';
      }

    #== 11.5 Se lleva a cabo el timbrado del XML ============================
    try {
            $client = new SoapClient($urlPAC);
            $client->__setLocation($urlLocation);
            $params = array('usuario' => $username, 'password' => $password, 'cfdiBase64'=>$b64, 'zip'=>False);
            $response = $client->__soapCall('timbrarBase64', array('parameters' => $params));
    } catch (SoapFault $fault) { 
			$j_array = array('code' => "400", "message" => $fault->faultcode."-".$fault->faultstring);
			$Resultado = json_encode($j_array);
			echo $Resultado;
			return;
    }    

    $ret = $response->return;

    if($ret->status != 200) {
		$j_array = array('code' => "500", "message" => "ERROR EN EL PROCESO DE TIMBRADO. ".$ret->status." - ".$ret->mensaje);
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

	/*
	if ($tipoTest > 0){
		echo '<br>';
		print_r($ret);
		echo '<br>';
		return;
	}
	*/
	
    #=== 11.7 Muestra los resultados del timbrado del CFDI =========================

    $RespServ = $ret->resultados->cfdiTimbrado;

    if($RespServ == NULL){
	    $j_array = array('code' => "600", "message" => "ERROR EN EL PROCESO DE TIMBRADO. status: ".$ret->status.", mensaje:  ".$ret->mensaje.".  ==> Resultado Timbrado: "."  status: ".$ret->resultados->status.", mensaje: ".$ret->resultados->mensaje);
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
/*		
		//echo $cadena_original;
		echo "status: ".$ret->resultados->status.", mensaje: ".$ret->resultados->mensaje;
		echo "<br>";
		echo "<br>";
		print_r($ret);
		echo "<br>";
		echo "<br>";
		die(PHP_EOL . "Error al timbrar el CFDI." . PHP_EOL . PHP_EOL);
*/
    }
	
	if ($tipoTest > 0){
		#== 12.5 Se muestra el .XML ya timbrado (CFDI V 3.2), opcional a mostrar =====
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'FACTURA .XML (CFDI) YA TIMBRADA';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo htmlspecialchars($RespServ);
		echo '</div><br>';              
	}
	
    ## 12. PROCESOS POSTERIORES AL TIMBRADO ########################################
    
    #== 12.1 Se asigna la respuesta del servidor a una variable de tipo DOM ====
    $VarXML = new DOMDocument();
    $VarXML->loadXML($RespServ);

    #== 12.2 Se graba la respuesta del servidor a un archivo .xml
    $VarXML->save($SendaCFDI."RespServ_".$NoFac.".xml");
    chmod($SendaCFDI."RespServ_".$NoFac.".xml", 0777);

    if($TipPDFGen == 1)
    {
        $NomArchXML = "CFDI_".$fact_serie."_".$invoiceNumber.".xml";
        $NomArchPDF = "CFDI_".$fact_serie."_".$invoiceNumber.".pdf";
    }
    else
    {
        $NomArchXML = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".xml";
        $NomArchPDF = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";
    }
	
    //$NomArchXML = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".xml";
    //$NomArchPDF = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";
	
    $xmlt = new DOMDocument();
    $xmlt->loadXML($RespServ);
    
    #== 12.1.1 Se valida si tiene addenda y se le agrega al CFDI ====
    if($incAddenda == 1)
    {

        if ($tipoTest > 0){

            echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
            echo 'DATOS DE LA ADDENDA';
            echo '</div>';
            echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
            echo 'tipoProveedor: <span style="color: #088A29;">'.$tip_Pro_AdAs.'</span><br>';
            echo 'folio: <span style="color: #088A29">'.$folio_AdAs.'</span><br>';
            echo 'ordenCompra: <span style="color: #088A29">'.$ord_Com_AdAs.'</span><br>';
            echo 'noProveedor: <span style="color: #088A29">'.$no_Prov_AdAs.'</span><br>';
            echo 'serie: <span style="color: #088A29">'.$serie_AdAs.'</span><br>';
            echo 'cuantos: <span style="color: #088A29">'.count($ajsonAddenda).'</span><br>';
            for ($i = 0; $i<count($ajsonAddenda); $i++){
                echo 'ivaDevengado ('.$i.'): <span style="color: #088A29">'.$ajsonAddenda[$i]["ivaDevengado"].'</span><br>';
                echo 'ivaAcreditable('.$i.'): <span style="color: #088A29">'.$ajsonAddenda[$i]["ivaAcreditable"].'</span><br>';
                echo 'noPartida('.$i.'): <span style="color: #088A29">'.$ajsonAddenda[$i]["noPartida"].'</span><br>';
                echo 'Otros('.$i.'): <span style="color: #088A29">'.$ajsonAddenda[$i]["Otros"].'</span><br>';
            };
            echo '</div><br>';              
        }
    
        $Root = $xmlt->firstChild;

        $addenda = $xmlt->createElement("cfdi:Addenda");
        $addenda = $Root->appendChild($addenda);
     
        $asonioscoc = $xmlt->createElement("ASONIOSCOC");
        $asonioscoc = $addenda->appendChild($asonioscoc);
        
        cargaAttNodo($asonioscoc, array(
           "tipoProveedor"=>$tip_Pro_AdAs,
           "folio"=>$folio_AdAs,
           "ordenCompra"=>$ord_Com_AdAs,
           "noProveedor"=>$no_Prov_AdAs,
           "serie"=>$serie_AdAs
           )
        );
     
        $partidas = $xmlt->createElement("Partidas");
        $partidas = $asonioscoc->appendChild($partidas);
     
        for ($i = 0; $i<count($ajsonAddenda); $i++){
            $partida = $xmlt->createElement("Partida");
            $partida = $partidas->appendChild($partida);
    
            cargaAttNodo($partida, array(
                "ivaDevengado"=>$ajsonAddenda[$i]["ivaDevengado"],
                "ivaAcreditable"=>$ajsonAddenda[$i]["ivaAcreditable"],
                "noPartida"=>$ajsonAddenda[$i]["noPartida"],
                "Otros"=>$ajsonAddenda[$i]["Otros"]
            ) );
        };
    }

    $xmlt->save($SendaCFDI.$NomArchXML); 
    chmod($SendaCFDI.$NomArchXML, 0777);
    #== 12.7 Procesos para extraer datos del Timbre Fiscal del CFDI =========
    $docXML = new DOMDocument();
    //$docXML->load($SendaCFDI."Fact_CFDI_".$NoFac.".xml");
    $docXML->load($SendaCFDI.$NomArchXML);
    $comprobante = $docXML->getElementsByTagName("TimbreFiscalDigital");

    #== 12.8 Se obtienen contenidos de los atributos y se asignan a variables para ser mostrados =======
    foreach($comprobante as $timFis){
            $version_timbre = $timFis->getAttribute('Version');
            $sello_SAT      = $timFis->getAttribute('SelloSAT');
            $cert_SAT       = $timFis->getAttribute('NoCertificadoSAT'); 
            $sello_CFD      = $timFis->getAttribute('SelloCFD'); 
            $tim_fecha      = $timFis->getAttribute('FechaTimbrado'); 
            $tim_uuid       = $timFis->getAttribute('UUID'); 
			$tim_RfcPac		= $timFis->getAttribute('RfcProvCertif'); 
    }

	if ($tipoTest > 0){
		echo 'Versión del timbre: <span style="color: #088A29;">'.$version_timbre.'</span><br>';
		echo 'Sello del SAT: <span style="color: #088A29">'.$sello_SAT.'</span><br>';
		echo 'Certificado del SAT: <span style="color: #088A29">'.$cert_SAT.'</span><br>';
		echo 'Sello del CFDI: <span style="color: #088A29">'.$sello_CFD.'</span><br>';
		echo 'Fecha de timbrado: <span style="color: #088A29">'.$tim_fecha.'</span><br>';
		echo 'Folio fiscal: <span style="color: #0830D2">'.$tim_uuid.'</span><br>';
		echo 'RFC del PAC: <span style="color: #0830D2">'.$tim_RfcPac.'</span><br>';
		echo 'No. de factura asignado por el sistema local: <span style="color: #B71616">'.$NoFac.'</span><br><br>';
	}
    
    #== 12.8.1 Se muestra el número de factura asignado por el sistema local (no asingado por el SAT).
    
    $params = $docXML->getElementsByTagName('Emisor');
    foreach ($params as $param) {
        $Emisor_RFC = $param->getAttribute('Rfc');
    }  
    
    $params = $docXML->getElementsByTagName('Receptor');
    foreach ($params as $param) {
        $Receptor_RFC = $param->getAttribute('Rfc');
    }              
    
    $params = $docXML->getElementsByTagName('Comprobante');
    foreach ($params as $param) {
        $total = $param->getAttribute('Total');
    }        
    
    #== 12.9 Se crea el archivo .PNG con codigo bidimensional =================================
	$filename = "archs_graf/Img_".$tim_uuid.".png";
	
    ### 13. CREACION DE PDF ###########################
    # 13.1 Creación del Archivo PDF 
    //$url = 'http://64.235.39.50/aptusCFDIRF/wsInvoicePDF_v33.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF;
    if($TipPDFGen == 1)
    {
        $url = 'https://aptuslegal.app/aptusCFDIRF/'.$FormatoPDF.'.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF.'&pInvNumb='.$invoiceNumber.'&pOrdCob='.$sOrdenCobro.'&pObserva='.$sObservaciones.'&pDirRecep='.$sDireRecep;;
    }
    else
    {
        $url = 'https://aptuslegal.app/aptusCFDIRF/'.$FormatoPDF.'.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF;
    }

    $objCurl = curl_init();
    curl_setopt($objCurl, CURLOPT_URL, $url);
    curl_setopt($objCurl, CURLOPT_HEADER, 0);
    curl_setopt($objCurl, CURLOPT_RETURNTRANSFER, true);
    curl_exec($objCurl);
    curl_close($objCurl);
    
	
    # 13.3 Envio del Archivos a Invoice de Zoho Books

    # Primero actualizamos el access_token de Books

    #== Datos y Variables para OAuth Token
	// $boa_ClientId 		= $abdDatGen["B_OAuth_client_id"];
	// $boa_ClientSecret 	= $abdDatGen["B_OAuth_client_secret"];
	// $boa_RefreshToken 	= $abdDatGen["B_OAuth_refresh_token"];
   //  $boa_GrantType      = $abdDatGen["B_OAuth_grant_type"];
   //  $boa_RedirectUri    = $abdDatGen["B_OAuth_redirect_uri"];
	// $boa_AuthUrl 		= "https://accounts.zoho.com/oauth/v2/token";

    #----------------------------------------------------------------
  	# JFA: 2021-06-12
	# Obtenemos el access_token
  	#----------------------------------------------------------------

    //  $Params = array(
    //     "refresh_token" => $boa_RefreshToken,
    //     "client_id" => $boa_ClientId,
    //     "client_secret" => $boa_ClientSecret,
    //     "redirect_uri" => $boa_RedirectUri,
    //     "grant_type" => $boa_GrantType
    // );
    
    // $curl = curl_init();
    // curl_setopt($curl, CURLOPT_URL, $boa_AuthUrl);
    // curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
    // curl_setopt($curl, CURLOPT_POSTFIELDS, $Params);
    // curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    // curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

    // $response = curl_exec($curl);
    // $array = json_decode($response);
    // $boa_access_token = $array->access_token;

   //  $boa_access_token = oauth($appOwner, 'ZBooks', $boa_RefreshToken, $boa_ClientId, $boa_ClientSecret, $boa_RedirectUri, $boa_GrantType, $boa_AuthUrl);

   $curl = curl_init();

   curl_setopt_array($curl, array(
     CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$organi_id_ZB,
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_ENCODING => '',
     CURLOPT_MAXREDIRS => 10,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_FOLLOWLOCATION => true,
     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
     CURLOPT_CUSTOMREQUEST => 'GET',
     CURLOPT_HTTPHEADER => array('Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c'),));
   
   $response = curl_exec($curl);
   
   curl_close($curl);

   $ra = json_decode($response);
   $boa_access_token = $ra->access_token;

    //echo $access_token;
    if($organi_id_ZB != "673221150")
    {
        # Primero el PDF
        $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchPDF;
        $request_url = 'https://www.zohoapis.com/books/v3/invoices/'.$invoiceID.'/attachment';

        if (function_exists('curl_file_create')) { // php 5.6+
            $cFile = curl_file_create($file_name_with_full_path);
        } 
        else 
        {  
            $cFile = '@' . realpath($file_name_with_full_path);
        }

        $post = array(
        // 'authtoken' => $authtoken_ZB,
        'organization_id' => $organi_id_ZB,
        'can_send_in_mail' => 'true',
        'attachment'=> $cFile);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $request_url);
        //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
        curl_setopt($ch, CURLOPT_POST, true); 
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        #JFA: Cambiamos el método de Autenticación a OAuth
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $boa_access_token)); 

        $r = curl_exec($ch);
        $resultPDF = $r;

        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close ($ch);
        $ra = json_decode($r);
        $resultPDF = $ra->code.' - '.$ra->message;
        if($ra->code != 0)
        {
            $NomArchHttp = '/var/www/html/aptusCFDIRF/archs_txt/'."CFDI_".$fact_serie."_".$invoiceNumber.".txt";
            $variable = fopen($NomArchHttp,"w");
            fwrite($variable,$httpcode . PHP_EOL);
            fwrite($variable,$ra->code.' - '.$ra->message);
            fclose($variable);
        }
    }
    //print_r($r);

	# Segundo el XML 
    $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchXML;
    $request_url = 'https://www.zohoapis.com/books/v3/invoices/'.$invoiceID.'/attachment';

    if (function_exists('curl_file_create')) { // php 5.6+
      $cFile = curl_file_create($file_name_with_full_path);
    } 
    else 
	{  
      $cFile = '@' . realpath($file_name_with_full_path);
    }


    $post = array(
    // 'authtoken' => $authtoken_ZB,
    'organization_id' => $organi_id_ZB,
    // 'can_send_in_mail' => 'true',
    'attachment'=> $cFile);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request_url);
    //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    #JFA: Cambiamos el método de Autenticación a OAuth
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $boa_access_token));

    $r = curl_exec($ch);
    //print_r($r);
    $resultXML = $r;

    curl_close ($ch);
	$ra = json_decode($r);
	$resultXML = $ra->code.' - '.$ra->message;


    # 13.4 Envio del Archivos a Invoice de Zoho Creator
	# Primero el PDF
    $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchPDF;
    //$request_url = 'https://creator.zoho.com/api/xml/fileupload/scope=creatorapi';
    # Actualización a API V2 y OAuth JFA y FFR 2021-06-18
    $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_I_query/'.$bRecId.'/File_CFDI_PDF/upload';

    if (function_exists('curl_file_create')) { // php 5.6+
      $cFile = curl_file_create($file_name_with_full_path);
    } 
    else 
	{  
      $cFile = '@' . realpath($file_name_with_full_path);
    }

    $post = array(
    // 'authtoken' => $authtoken_ZC,
    // 'applinkname' => $applnkname,
    // 'formname' => 'CreacionCFDIv33',
	// 'fieldname' => 'File_CFDI_PDF',
    // 'recordId' => $bRecId,
    // 'filename' => $NomArchPDF,
    'file'=> $cFile);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request_url);
    //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    #JFA: Cambiamos el método de Autenticación a OAuth
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $access_token));

    $r = curl_exec($ch);
    curl_close ($ch);
	
	# Segundo el XML
    $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchXML;
    //$request_url = 'https://creator.zoho.com/api/xml/fileupload/scope=creatorapi';
    # Actualización a API V2 y OAuth JFA y FFR 2021-06-18
    $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_I_query/'.$bRecId.'/File_CFDI_XML/upload';

    if (function_exists('curl_file_create')) { // php 5.6+
      $cFile = curl_file_create($file_name_with_full_path);
    } 
    else 
	{  
      $cFile = '@' . realpath($file_name_with_full_path);
    }

    $post = array(
    // 'authtoken' => $authtoken_ZC,
    // 'applinkname' => $applnkname,
    // 'formname' => 'CreacionCFDIv33',
	// 'fieldname' => 'File_CFDI_XML',
    // 'recordId' => $bRecId,
    // 'filename' => $NomArchXML,
    'file'=> $cFile);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request_url);
    //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 
    #JFA: Cambiamos el método de Autenticación a OAuth
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $access_token));

    $r = curl_exec($ch);
    curl_close ($ch);

	
    #== Se elimina los archivos de trabajo
    unlink($SendaCFDI."PreCFDI-33_".$NoFac.".xml");
    unlink($SendaCFDI."RespServ_".$NoFac.".xml"); 
    unlink($filename); 
	
	
    $j_array = array('code' => "200", 'message' => "Proceso de creacion de CFDI fue exitoso", 'version_timbre' => $version_timbre,
						'cert_SAT' => $cert_SAT, 'tim_fecha' => $tim_fecha, 'tim_uuid' => $tim_uuid, 'tim_RfcPac' => $tim_RfcPac,
						'sello_SAT' => $sello_SAT, 'sello_CFD' => $sello_CFD, 'RespBooks_FileXML' => $resultXML, 'RespBooks_FilePDF' => $resultPDF);
    $Resultado = json_encode($j_array);
	echo $Resultado;
	return;
	
    
    ### 14. FUNCIONES DEL MÓDULO #########################################################
    # 14.1 Función que integra los nodos al archivo .XML y forma la "Cadena original".
    function cargaAtt(&$nodo, $attr){
        global $xml, $cadena_original;
        $quitar = array('sello'=>1,'noCertificado'=>1,'certificado'=>1);
        foreach ($attr as $key => $val){
            $val = preg_replace('/\s\s+/', ' ', $val);
            $val = trim($val);
            if (strlen($val)>0){
                 $val = utf8_encode(str_replace("|","/",$val));
                 $nodo->setAttribute($key,$val);
                 if (!isset($quitar[$key])) 
                   if (substr($key,0,3) != "xml" &&
                       substr($key,0,4) != "xsi:")
                    $cadena_original .= $val . "|";
            }
         }
     }

    
    # 14.2 Funciónes que da formato al "Importe total" como lo requiere el SAT para ser integrado al código QR.
     
    function ProcesImpTot($ImpTot){
        $ImpTot = number_format($ImpTot, 4); // <== Se agregó el 30 de abril de 2017.
        $ArrayImpTot = explode(".", $ImpTot);
        $NumEnt = $ArrayImpTot[0];
        $NumDec = ProcesDecFac($ArrayImpTot[1]);
        
        return $NumEnt.".".$NumDec;
    }
    
    function ProcesDecFac($Num){
        $FolDec = "";
        if ($Num < 10){$FolDec = "00000".$Num;}
        if ($Num > 9 and $Num < 100){$FolDec = $Num."0000";}
        if ($Num > 99 and $Num < 1000){$FolDec = $Num."000";}
        if ($Num > 999 and $Num < 10000){$FolDec = $Num."00";}
        if ($Num > 9999 and $Num < 100000){$FolDec = $Num."0";}
        return $FolDec;
    }        
	
	function limpioCaracteresXML($cadena){
		$search  = array("<", ">", "&", "'");
		$replace = array("&lt;", "&gt", "&amp;", "&apos");
		$final = str_replace($search, $replace, $cadena);
		return $final;
	}
    
