/**
  * Método que guarda el XML del Recibo de un intercambio
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-12-23
  */
 public function saveXML($Emisor, $xml)
 {
     // crear recibo
     $EnvioRecibos = new \sasco\LibreDTE\Sii\EnvioRecibos();
     $EnvioRecibos->loadXML($xml);
     if (!$EnvioRecibos->getID()) {
         return null;
     }
     $Caratula = $EnvioRecibos->toArray()['EnvioRecibos']['SetRecibos']['Caratula'];
     if (explode('-', $Caratula['RutRecibe'])[0] != $Emisor->rut) {
         return false;
     }
     // guardar recibo
     $this->db->beginTransaction();
     $this->responde = explode('-', $Caratula['RutResponde'])[0];
     $this->recibe = $Emisor->rut;
     $this->codigo = md5($xml);
     $this->contacto = !empty($Caratula['NmbContacto']) ? substr($Caratula['NmbContacto'], 0, 40) : null;
     $this->telefono = !empty($Caratula['FonoContacto']) ? substr($Caratula['FonoContacto'], 0, 40) : null;
     $this->email = !empty($Caratula['MailContacto']) ? substr($Caratula['MailContacto'], 0, 80) : null;
     $this->fecha_hora = str_replace('T', ' ', $Caratula['TmstFirmaEnv']);
     $this->xml = base64_encode($xml);
     if (!$this->save()) {
         $this->db->rollback();
         return false;
     }
     // procesar cada recibo
     foreach ($EnvioRecibos->getRecibos() as $Recibo) {
         // si el RUT del emisor no corresponde con el del contribuyente el
         // acuse no es para este
         if (explode('-', $Recibo['DocumentoRecibo']['RUTEmisor'])[0] != $Emisor->rut) {
             $this->db->rollback();
             return false;
         }
         // buscar DTE emitido en el ambiente del emisor
         $DteEmitido = new Model_DteEmitido($Emisor->rut, $Recibo['DocumentoRecibo']['TipoDoc'], $Recibo['DocumentoRecibo']['Folio'], $Emisor->certificacion);
         // si no existe o si los datos del DTE emitido no corresponden error
         if (!$DteEmitido->exists() or explode('-', $Recibo['DocumentoRecibo']['RUTRecep'])[0] != $DteEmitido->receptor or $Recibo['DocumentoRecibo']['FchEmis'] != $DteEmitido->fecha or $Recibo['DocumentoRecibo']['MntTotal'] != $DteEmitido->total) {
             $this->db->rollback();
             return false;
         }
         // guardar recibo para el DTE
         $DteIntercambioReciboDte = new Model_DteIntercambioReciboDte($DteEmitido->emisor, $DteEmitido->dte, $DteEmitido->folio, $DteEmitido->certificacion);
         $DteIntercambioReciboDte->responde = $this->responde;
         $DteIntercambioReciboDte->codigo = $this->codigo;
         $DteIntercambioReciboDte->recinto = substr($Recibo['DocumentoRecibo']['Recinto'], 0, 80);
         $DteIntercambioReciboDte->firma = substr($Recibo['DocumentoRecibo']['RutFirma'], 0, 10);
         $DteIntercambioReciboDte->fecha_hora = str_replace('T', ' ', $Recibo['DocumentoRecibo']['TmstFirmaRecibo']);
         if (!$DteIntercambioReciboDte->save()) {
             $this->db->rollback();
             return false;
         }
     }
     // aceptar transacción
     $this->db->commit();
     return true;
 }
 /**
  * Acción que genera los datos del archivo EnvioRecibos del intercambio
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-11
  */
 private function intercambio_EnvioRecibos($EnvioDTE, $caratula, $Firma)
 {
     // objeto EnvioRecibo, asignar carátula y Firma
     $EnvioRecibos = new \sasco\LibreDTE\Sii\EnvioRecibos();
     $EnvioRecibos->setCaratula($caratula);
     $EnvioRecibos->setFirma($Firma);
     // procesar cada DTE
     foreach ($EnvioDTE->getDocumentos() as $DTE) {
         $EnvioRecibos->agregar(['TipoDoc' => $DTE->getTipo(), 'Folio' => $DTE->getFolio(), 'FchEmis' => $DTE->getFechaEmision(), 'RUTEmisor' => $DTE->getEmisor(), 'RUTRecep' => $DTE->getReceptor(), 'MntTotal' => $DTE->getMontoTotal(), 'Recinto' => 'Oficina central', 'RutFirma' => $Firma->getID()]);
     }
     // generar XML
     $xml = $EnvioRecibos->generar();
     // validar schema del XML que se generó
     if (!$EnvioRecibos->schemaValidate()) {
         return false;
     }
     // entregar xml
     return $xml;
 }
header('Content-type: text/plain; charset=ISO-8859-1');
// incluir archivos php de la biblioteca y configuraciones
include 'inc.php';
// datos
$archivo_recibido = 'xml/intercambio/ENVIO_DTE_420328.xml';
$RutResponde = '76192083-9';
$RutFirma = '';
// Cargar EnvioDTE y extraer arreglo con datos de carátula y DTEs
$EnvioDte = new \sasco\LibreDTE\Sii\EnvioDte();
$EnvioDte->loadXML(file_get_contents($archivo_recibido));
$Caratula = $EnvioDte->getCaratula();
$Documentos = $EnvioDte->getDocumentos();
// caratula
$caratula = ['RutResponde' => $RutResponde, 'RutRecibe' => $Caratula['RutEmisor']];
// objeto EnvioRecibo, asignar carátula y Firma
$EnvioRecibos = new \sasco\LibreDTE\Sii\EnvioRecibos();
$EnvioRecibos->setCaratula($caratula);
$EnvioRecibos->setFirma(new \sasco\LibreDTE\FirmaElectronica($config['firma']));
// procesar cada DTE
foreach ($Documentos as $DTE) {
    $EnvioRecibos->agregar(['TipoDoc' => $DTE->getTipo(), 'Folio' => $DTE->getFolio(), 'FchEmis' => $DTE->getFechaEmision(), 'RUTEmisor' => $DTE->getEmisor(), 'RUTRecep' => $DTE->getReceptor(), 'MntTotal' => $DTE->getMontoTotal(), 'Recinto' => 'Oficina central', 'RutFirma' => $RutFirma]);
}
// generar XML
$xml = $EnvioRecibos->generar();
// validar schema del XML que se generó
if ($EnvioRecibos->schemaValidate()) {
    // mostrar XML al usuario, deberá ser guardado y subido al SII en:
    // https://www4.sii.cl/pfeInternet
    echo $xml;
}
// si hubo errores mostrar
 /**
  * Acción que procesa y responde al intercambio recibido
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-10-08
  */
 public function responder($codigo)
 {
     $Emisor = $this->getContribuyente();
     // si no se viene por post error
     if (!isset($_POST['submit'])) {
         \sowerphp\core\Model_Datasource_Session::message('No puede acceder de forma directa a ' . $this->request->request, 'error');
         $this->redirect(str_replace('responder', 'ver', $this->request->request));
     }
     // obtener DTE emitido
     $DteIntercambio = new Model_DteIntercambio($Emisor->rut, $codigo, (int) $Emisor->certificacion);
     if (!$DteIntercambio->exists()) {
         \sowerphp\core\Model_Datasource_Session::message('No existe el intercambio solicitado', 'error');
         $this->redirect('/dte/dte_intercambios');
     }
     // obtener firma
     $Firma = $Emisor->getFirma($this->Auth->User->id);
     if (!$Firma) {
         \sowerphp\core\Model_Datasource_Session::message('No hay firma electrónica asociada a la empresa (o bien no se pudo cargar), debe agregar su firma antes de generar DTE', 'error');
         $this->redirect('/dte/admin/firma_electronicas');
     }
     //
     // construir RecepcionDTE
     //
     $RecepcionDTE = [];
     $n_dtes = count($_POST['TipoDTE']);
     for ($i = 0; $i < $n_dtes; $i++) {
         $RecepcionDTE[] = ['TipoDTE' => $_POST['TipoDTE'][$i], 'Folio' => $_POST['Folio'][$i], 'FchEmis' => $_POST['FchEmis'][$i], 'RUTEmisor' => $_POST['RUTEmisor'][$i], 'RUTRecep' => $_POST['RUTRecep'][$i], 'MntTotal' => $_POST['MntTotal'][$i], 'EstadoRecepDTE' => $_POST['EstadoRecepDTE'][$i], 'RecepDTEGlosa' => $_POST['RecepDTEGlosa'][$i]];
     }
     // armar respuesta de envío
     $EnvioDte = new \sasco\LibreDTE\Sii\EnvioDte();
     $EnvioDte->loadXML(base64_decode($DteIntercambio->archivo_xml));
     $Caratula = $EnvioDte->getCaratula();
     $RespuestaEnvio = new \sasco\LibreDTE\Sii\RespuestaEnvio();
     $RespuestaEnvio->agregarRespuestaEnvio(['NmbEnvio' => $DteIntercambio->archivo, 'CodEnvio' => $DteIntercambio->codigo, 'EnvioDTEID' => $EnvioDte->getID(), 'Digest' => $EnvioDte->getDigest(), 'RutEmisor' => $EnvioDte->getEmisor(), 'RutReceptor' => $EnvioDte->getReceptor(), 'EstadoRecepEnv' => $_POST['EstadoRecepEnv'], 'RecepEnvGlosa' => $_POST['RecepEnvGlosa'], 'NroDTE' => count($RecepcionDTE), 'RecepcionDTE' => $RecepcionDTE]);
     // asignar carátula y Firma
     $RespuestaEnvio->setCaratula(['RutResponde' => $Emisor->rut . '-' . $Emisor->dv, 'RutRecibe' => $Caratula['RutEmisor'], 'IdRespuesta' => $DteIntercambio->codigo, 'NmbContacto' => $_POST['NmbContacto'], 'MailContacto' => $_POST['MailContacto']]);
     $RespuestaEnvio->setFirma($Firma);
     // generar y validar XML
     $RecepcionDTE_xml = $RespuestaEnvio->generar();
     if (!$RespuestaEnvio->schemaValidate()) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible generar RecepcionDTE.<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect(str_replace('responder', 'ver', $this->request->request));
     }
     //
     // generar EnvioRecibos
     //
     $EnvioRecibos = new \sasco\LibreDTE\Sii\EnvioRecibos();
     $EnvioRecibos->setCaratula(['RutResponde' => $Emisor->rut . '-' . $Emisor->dv, 'RutRecibe' => $Caratula['RutEmisor'], 'NmbContacto' => $_POST['NmbContacto'], 'MailContacto' => $_POST['MailContacto']]);
     $EnvioRecibos->setFirma($Firma);
     // procesar cada DTE
     $EnvioRecibos_r = [];
     for ($i = 0; $i < $n_dtes; $i++) {
         if ($_POST['acuse'][$i]) {
             $EnvioRecibos->agregar(['TipoDoc' => $_POST['TipoDTE'][$i], 'Folio' => $_POST['Folio'][$i], 'FchEmis' => $_POST['FchEmis'][$i], 'RUTEmisor' => $_POST['RUTEmisor'][$i], 'RUTRecep' => $_POST['RUTRecep'][$i], 'MntTotal' => $_POST['MntTotal'][$i], 'Recinto' => $_POST['Recinto'], 'RutFirma' => $Firma->getID()]);
             $EnvioRecibos_r[] = 'T' . $_POST['TipoDTE'][$i] . 'F' . $_POST['Folio'][$i];
         }
     }
     // generar y validar XML
     if ($EnvioRecibos_r) {
         $EnvioRecibos_xml = $EnvioRecibos->generar();
         if (!$EnvioRecibos->schemaValidate()) {
             \sowerphp\core\Model_Datasource_Session::message('No fue posible generar EnvioRecibos.<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
             $this->redirect(str_replace('responder', 'ver', $this->request->request));
         }
     }
     //
     // generar ResultadoDTE
     //
     // objeto para la respuesta
     $RespuestaEnvio = new \sasco\LibreDTE\Sii\RespuestaEnvio();
     // procesar cada DTE
     for ($i = 0; $i < $n_dtes; $i++) {
         $estado = !$_POST['EstadoRecepDTE'][$i] ? 0 : 2;
         $RespuestaEnvio->agregarRespuestaDocumento(['TipoDTE' => $_POST['TipoDTE'][$i], 'Folio' => $_POST['Folio'][$i], 'FchEmis' => $_POST['FchEmis'][$i], 'RUTEmisor' => $_POST['RUTEmisor'][$i], 'RUTRecep' => $_POST['RUTRecep'][$i], 'MntTotal' => $_POST['MntTotal'][$i], 'CodEnvio' => $i + 1, 'EstadoDTE' => $estado, 'EstadoDTEGlosa' => \sasco\LibreDTE\Sii\RespuestaEnvio::$estados['respuesta_documento'][$estado]]);
     }
     // asignar carátula y Firma
     $RespuestaEnvio->setCaratula(['RutResponde' => $Emisor->rut . '-' . $Emisor->dv, 'RutRecibe' => $Caratula['RutEmisor'], 'IdRespuesta' => $DteIntercambio->codigo, 'NmbContacto' => $_POST['NmbContacto'], 'MailContacto' => $_POST['MailContacto']]);
     $RespuestaEnvio->setFirma($Firma);
     // generar y validar XML
     $ResultadoDTE_xml = $RespuestaEnvio->generar();
     if (!$RespuestaEnvio->schemaValidate()) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible generar ResultadoDTE.<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect(str_replace('responder', 'ver', $this->request->request));
     }
     //
     // guardar estado del intercambio y usuario que lo procesó
     //
     $DteIntercambio->estado = (int) $_POST['EstadoRecepEnv'];
     $DteIntercambio->recepcion_xml = base64_encode($RecepcionDTE_xml);
     if (isset($EnvioRecibos_xml)) {
         $DteIntercambio->recibos_xml = base64_encode($EnvioRecibos_xml);
     }
     $DteIntercambio->resultado_xml = base64_encode($ResultadoDTE_xml);
     $DteIntercambio->fecha_hora_respuesta = date('Y-m-d H:i:s');
     $DteIntercambio->usuario = $this->Auth->User->id;
     $DteIntercambio->save();
     //
     // guardar documentos que han sido aceptados con acuse de recibo
     //
     if (isset($EnvioRecibos_xml)) {
         // actualizar datos del emisor si no tine usuario asociado
         $EmisorIntercambio = $DteIntercambio->getEmisor();
         if (!$EmisorIntercambio->usuario) {
             $emisor = $DteIntercambio->getDocumentos()[0]->getDatos()['Encabezado']['Emisor'];
             $EmisorIntercambio->razon_social = $emisor['RznSoc'];
             if (!empty($emisor['GiroEmis'])) {
                 $EmisorIntercambio->giro = $emisor['GiroEmis'];
             }
             if (!empty($emisor['CorreoEmisor'])) {
                 $EmisorIntercambio->email = $emisor['CorreoEmisor'];
             }
             if (!empty($emisor['Acteco'])) {
                 $actividad_economica = $EmisorIntercambio->actividad_economica;
                 $EmisorIntercambio->actividad_economica = $emisor['Acteco'];
                 if (!$EmisorIntercambio->getActividadEconomica()->exists()) {
                     $EmisorIntercambio->actividad_economica = $actividad_economica;
                 }
             }
             $comuna = (new \sowerphp\app\Sistema\General\DivisionGeopolitica\Model_Comunas())->getComunaByName($emisor['CmnaOrigen']);
             if ($comuna) {
                 $EmisorIntercambio->direccion = $emisor['DirOrigen'];
                 $EmisorIntercambio->comuna = $comuna;
             }
             if (!empty($emisor['CdgSIISucur'])) {
                 $EmisorIntercambio->sucursal_sii = (int) $emisor['CdgSIISucur'];
             }
             $EmisorIntercambio->modificado = date('Y-m-d H:i:s');
             $EmisorIntercambio->save();
         }
         // guardar documentos que tienen acuse de recibo como dte recibidos
         $Documentos = $DteIntercambio->getDocumentos();
         foreach ($Documentos as $Dte) {
             if (in_array($Dte->getID(), $EnvioRecibos_r)) {
                 $resumen = $Dte->getResumen();
                 $DteRecibido = new Model_DteRecibido();
                 $DteRecibido->emisor = $DteIntercambio->getEmisor()->rut;
                 $DteRecibido->dte = $resumen['TpoDoc'];
                 $DteRecibido->folio = $resumen['NroDoc'];
                 $DteRecibido->certificacion = (int) $DteIntercambio->certificacion;
                 if (!$DteRecibido->exists()) {
                     $DteRecibido->receptor = $Emisor->rut;
                     $DteRecibido->tasa = (int) $resumen['TasaImp'];
                     $DteRecibido->fecha = $resumen['FchDoc'];
                     $DteRecibido->sucursal_sii = (int) $resumen['CdgSIISucur'];
                     if ($resumen['MntExe']) {
                         $DteRecibido->exento = $resumen['MntExe'];
                     }
                     if ($resumen['MntNeto']) {
                         $DteRecibido->neto = $resumen['MntNeto'];
                     }
                     $DteRecibido->iva = (int) $resumen['MntIVA'];
                     $DteRecibido->total = (int) $resumen['MntTotal'];
                     $DteRecibido->usuario = $this->Auth->User->id;
                     $DteRecibido->intercambio = $DteIntercambio->codigo;
                     $DteRecibido->save();
                 }
             }
         }
     }
     //
     // enviar los 3 XML de respuesta por email
     //
     $email = $Emisor->getEmailSmtp();
     $email->to($_POST['responder_a']);
     $email->subject($Emisor->rut . '-' . $Emisor->dv . ' - Respuesta intercambio N° ' . $DteIntercambio->codigo);
     foreach (['RecepcionDTE', 'EnvioRecibos', 'ResultadoDTE'] as $xml) {
         if (isset(${$xml . '_xml'})) {
             $email->attach(['data' => ${$xml . '_xml'}, 'name' => $xml . '_' . $Emisor->rut . '-' . $Emisor->dv . '_' . $DteIntercambio->codigo . '.xml', 'type' => 'application/xml']);
         }
     }
     // enviar email
     $status = $email->send('Se adjuntan XMLs de respuesta a intercambio de DTE.');
     if ($status === true) {
         \sowerphp\core\Model_Datasource_Session::message('Se procesaron DTEs de intercambio y se envió la respuesta a: ' . $_POST['responder_a'], 'ok');
     } else {
         \sowerphp\core\Model_Datasource_Session::message('Se procesaron DTEs de intercambio, pero no fue posible enviar el email, por favor intente nuevamente.<br /><em>' . $status['message'] . '</em>', 'error');
     }
     $this->redirect(str_replace('responder', 'ver', $this->request->request));
 }