<?php
    /**
     *  wsCreditNoteSFJ_VP_v40_RR.PHP
     *  
     *  Autor: Felipe Faccinetto
     *  Fecha: Marzo/2019
     * 
     *  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
	 *  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
     *  pTipPDFGen      Especifica si se utiliza un formato en especial o no, 0 = No, 1 = Arochi
     *  pAppOwner       Especifica el propietario de la aplicación que se actualizara.
     * 
    */
    
    header('Content-Type: text/html; charset=UTF-8');

    include("qrlib/qrlib.php");
    require('oauth.php');

    $bDatPAC 	= "";
    $bDatGen 	= "";
    $bDatEmi 	= "";
    $bDatRec 	= "";
    $bDatArt 	= "";
    $bDatRel 	= "";
	$bRecId  	= "";
	$FormatoPDF = "wsInvoicePDF_v33";
	//$FormatoPDF = "wsInvoicePDF_VP_v33";
    $applnkname = 'cfdi'; 
    $TipPDFGen  = 0;
    $appOwner   = "";

    ## 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['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($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 == "")
    {
		$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);
	}

    #== 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);
	}
	
	$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'); // 

    # 1.2 Muestra la zona horaria predeterminada del servidor (opcional a mostrar)

    ### 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";
    }
    else
    {
        ## Timbrado en pruebas
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
    }
    
    $username = $abdDatPAC["username"];
    $password = $abdDatPAC["password"];

    $valorNod = '';
    $metodoDePago  = "";
    
    ### MUESTRA LOS DATOS DEL USUARIO QUE ESTÁ TIMBRANDO (OPCIONAL A MOSTRAR) ######
   
    
    ### 3. DEFINICIÓN DE VARIABLES INICIALES ##########################################

    //$noCertificado = "00001000000409970859";
    //$file_cer      = "00001000000409970859.cer.pem";  
    //$file_key      = "00001000000409970859.key.pem";  

    $noCertificado = $abdDatGen["Certificado_SAT"];
    $file_cer      = $abdDatGen["Archivo_CER"];
    $file_key      = $abdDatGen["Archivo_KEY"];
	
	$organi_id_ZB  = $abdDatGen["organization_id_ZB"];
	$authtoken_ZB  = $abdDatGen["authtoken_ZB"];
	$authtoken_ZC  = $abdDatGen["authtoken_ZC"];

    #== Datos y Variables para OAuth Token
	$coa_ClientId 		= $abdDatGen["C_OAuth_client_id"];
	$coa_ClientSecret 	= $abdDatGen["C_OAuth_client_secret"];
	$coa_RefreshToken 	= $abdDatGen["C_OAuth_refresh_token"];
    $coa_GrantType      = $abdDatGen["C_OAuth_grant_type"];
    $coa_RedirectUri    = $abdDatGen["C_OAuth_redirect_uri"];
	$coa_AuthUrl 		= "https://accounts.zoho.com/oauth/v2/token";

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

        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/f/'.$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'
        $coa_access_token = $data['access_token'];

    #== Conexión a Zoho Creator para el detalle del CFDI
    # Actualización a API V2 con OAuth. JFA y FFR 2021-06-18
    $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_CN_query/'.$bRecId;
    

    $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_FOLLOWLOCATION, true);
    #JFA: Cambiamos el método de Autenticación a OAuth
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken '. $coa_access_token));

    $r = curl_exec($ch);

	$array = json_decode($r);

    print_r($array);
	
	$bdDatArt =  $array->data->jsonInvoiceDetail;

	$abdDatArt = json_decode($bdDatArt,true);

    //print_r($abdDatArt);

	//echo $bdDatLed;
	
    curl_close ($ch);

	
    ### 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"];
    $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.
    
    // 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 ($appOwner == "arochiylindner.aptus")
    {
        cargaAtt($root, array(
            "Version"=>"4.0", 
            "Serie"=>$fact_serie,
            "Folio"=>$fact_folio,
            "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"=>$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
            )
        );
    }

    $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
                )
            );
    
	// 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"])));
		}
	}


    $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;
    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"=>utf8_decode($abdDatArt[$i]["descUnidad"]),
               "Descripcion"=>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"])
            )
        );
    
		$impuestos = $xml->createElement("cfdi:Impuestos");
		$impuestos = $concepto->appendChild($impuestos);

		if ($abdDatArt[$i]["ivaImporte"] > 0)
		{
			$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)
		{
			$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,'.','')
				) 
			);
		}
	}
	
	$Impuestos = $xml->createElement("cfdi:Impuestos");
	$Impuestos = $root->appendChild($Impuestos);

	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,'.','')
							)
						);
	}
	
	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,'.','')
					)
				);
    }        
            
	
    $complemento = $xml->createElement("cfdi:Complemento");
    $complemento = $root->appendChild($complemento);
    
    #== 10.7 Termina de conformarse la "Cadena original" con doble ||
    $cadena_original .= "|";   
	
    #=== Muestra la cadena original (opcional a mostrar) =======================
    
    #== 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);    
    
    #=== Muestra el sello (opcional a mostrar) =================================
    
    #== 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;
    } 
    
    #=== Muestra el certificado del sello digital (opcional a mostrar) =========
    
    #== 10.11 Se continua con la integración de nodos ===========================   
    $root->setAttribute("sello",$sello);
    $root->setAttribute("certificado",$certificado);   # Certificado.
	
    //$root->setAttribute("motivoDescuento",$MotivDesc); # Motivo de descuento. 
    //$root->setAttribute("folio",$fact_folio);          # Folio de la factura (número entero).
    //$root->setAttribute("serie",$fact_serie);          # Serie de la factura (letra o letras).
    
    #== Fin de la integración de nodos =========================================
    
    
    #=== 10.12 Se guarda el archivo .XML antes de ser timbrado =======================
	
    $NomArchCFDI = $SendaCFDI."PreCFDI-33_".$NoFac.".xml";
    $NomArchPDF1 = $SendaCFDI."VP_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";
	
    $NomArchXML = "PreCFDI-33_".$NoFac.".xml";
    $NomArchPDF = "VP_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";

//    $cfdi = $xml->saveXML();
    $xml->formatOutput = true;             
    $xml->save($NomArchCFDI); // Guarda el archivo .XML (sin timbrar) en el directorio predeterminado.
    unset($xml);
    
    #=== 10.13 Se dan permisos de escritura al archivo .xml. =========================
    chmod($NomArchCFDI, 0777); 

    ### 13. CREACION DE PDF Y ENVIO DE CORREO CON XML Y PDF ###########################
    # 13.1 Creación del Archivo PDF
    //$url = 'http://localhost/aptusCFDIRF/wsInvoicePDF_VP_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;
    }
	echo $url;
	echo "<br>";
    $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 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 con OAuth. JFA y FFR 2021-06-18
    $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/CFDI_Egreso_Borradores/'.$bRecId.'/File_PDF_VP/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);
    }

/*
	echo $bRecId;
	echo "<br>";
	echo $authtoken_ZC;
	echo "<br>";
	echo $NomArchPDF;
	echo "<br>";
	echo $file_name_with_full_path;
	echo "<br>";
*/	

    $post = array(
    // 'authtoken' => $authtoken_ZC,							
    // 'applinkname' => $applnkname,
    // 'formname' => 'Nota_Credito',
	// 'fieldname' => 'File_PDF_VP',
    // '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);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $coa_access_token));

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

    #== Se elimina los archivos de trabajo
    //unlink($NomArchCFDI);
    //unlink($NomArchPDF1);
	
    $j_array = array('code' => "200", "message" => "Proceso de creacion de CFDI fue exitoso", 'file_upload' => $NomArchPDF);
    $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){
        $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;
    }        
    
