Ejemplo n.º 1
0
 /**
  * Método que genera el XML para el envío de la respuesta al SII
  * @param caratula Arreglo con la carátula de la respuesta
  * @param Firma Objeto con la firma electrónica
  * @return XML con la respuesta firmada o =false si no se pudo generar o firmar la respuesta
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public function generar()
 {
     // si ya se había generado se entrega directamente
     if ($this->xml_data) {
         return $this->xml_data;
     }
     // si no hay respuestas para generar entregar falso
     if (!isset($this->recibos[0])) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIORECIBOS_FALTA_RECIBO, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIORECIBOS_FALTA_RECIBO));
         return false;
     }
     // si no hay carátula error
     if (!$this->caratula) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIORECIBOS_FALTA_CARATULA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIORECIBOS_FALTA_CARATULA));
         return false;
     }
     // crear arreglo de lo que se enviará
     $xmlEnvio = (new \sasco\LibreDTE\XML())->generate(['EnvioRecibos' => ['@attributes' => ['xmlns' => 'http://www.sii.cl/SiiDte', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://www.sii.cl/SiiDte EnvioRecibos_v10.xsd', 'version' => '1.0'], 'SetRecibos' => ['@attributes' => ['ID' => 'SetDteRecibidos'], 'Caratula' => $this->caratula, 'Recibo' => null]]])->saveXML();
     // generar cada recibo y firmar
     $Recibos = [];
     foreach ($this->recibos as &$recibo) {
         $recibo_xml = new \sasco\LibreDTE\XML();
         $recibo_xml->generate(['Recibo' => $recibo]);
         $recibo_firmado = $this->Firma ? $this->Firma->signXML($recibo_xml->saveXML(), '#' . $recibo['DocumentoRecibo']['@attributes']['ID'], 'DocumentoRecibo', true) : $recibo_xml->saveXML();
         $Recibos[] = trim(str_replace('<?xml version="1.0" encoding="ISO-8859-1"?>', '', $recibo_firmado));
     }
     // firmar XML del envío y entregar
     $xml = str_replace('<Recibo/>', implode("\n", $Recibos), $xmlEnvio);
     $this->xml_data = $this->Firma ? $this->Firma->signXML($xml, '#SetDteRecibidos', 'SetRecibos', true) : $xml;
     return $this->xml_data;
 }
Ejemplo n.º 2
0
 public function caso3()
 {
     \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIO_USUARIO_INCORRECTO, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIO_USUARIO_INCORRECTO));
     \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIO_ERROR_XML, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIO_ERROR_XML, 'No hay XML'));
     \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::REQUEST_ERROR_BODY, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::REQUEST_ERROR_BODY, 'getToken', 10));
     \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIO_NO_AUTENTICADO, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIO_NO_AUTENTICADO));
 }
Ejemplo n.º 3
0
 /**
  * Método que verifica el código de autorización de folios
  * @return =true si está ok el XML cargado
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-10-30
  */
 public function check()
 {
     // validar firma del SII sobre los folios
     $firma = $this->getFirma();
     $idk = $this->getIDK();
     if (!$firma or !$idk) {
         return false;
     }
     $pub_key = \sasco\LibreDTE\Sii::cert($idk);
     if (!$pub_key or openssl_verify($this->xml->getFlattened('/AUTORIZACION/CAF/DA'), base64_decode($firma), $pub_key) !== 1) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::FOLIOS_ERROR_FIRMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::FOLIOS_ERROR_FIRMA));
         return false;
     }
     // validar clave privada y pública proporcionada por el SII
     $private_key = $this->getPrivateKey();
     if (!$private_key) {
         return false;
     }
     $plain = md5(date('U'));
     if (!openssl_private_encrypt($plain, $crypt, $private_key)) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::FOLIOS_ERROR_ENCRIPTAR, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::FOLIOS_ERROR_ENCRIPTAR));
         return false;
     }
     $public_key = $this->getPublicKey();
     if (!$public_key) {
         return false;
     }
     if (!openssl_public_decrypt($crypt, $plain_firmado, $public_key)) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::FOLIOS_ERROR_DESENCRIPTAR, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::FOLIOS_ERROR_DESENCRIPTAR));
         return false;
     }
     return $plain === $plain_firmado;
 }
Ejemplo n.º 4
0
 /**
  * Método que realiza el envío del documento al SII
  * @return Track ID del envío o =false si hubo algún problema al enviar el documento
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-12-14
  */
 public function enviar()
 {
     // generar XML que se enviará
     if (!$this->xml_data) {
         $this->xml_data = $this->generar();
     }
     if (!$this->xml_data) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::DOCUMENTO_ERROR_GENERAR_XML, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::DOCUMENTO_ERROR_GENERAR_XML, substr(get_class($this), strrpos(get_class($this), '\\') + 1)));
         return false;
     }
     // validar schema del documento antes de enviar
     if (!$this->schemaValidate()) {
         return false;
     }
     // solicitar token
     $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($this->Firma);
     if (!$token) {
         return false;
     }
     // enviar DTE
     $envia = $this->caratula['RutEnvia'];
     $emisor = !empty($this->caratula['RutEmisor']) ? $this->caratula['RutEmisor'] : $this->caratula['RutEmisorLibro'];
     $result = \sasco\LibreDTE\Sii::enviar($envia, $emisor, $this->xml_data, $token);
     if ($result === false) {
         return false;
     }
     if (!is_numeric((string) $result->TRACKID)) {
         return false;
     }
     return (int) (string) $result->TRACKID;
 }
Ejemplo n.º 5
0
 /**
  * Método que genera la previsualización del PDF del DTE
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]delaf.cl)
  * @version 2015-09-24
  */
 public function pdf($receptor, $dte, $codigo)
 {
     $Emisor = $this->getContribuyente();
     // obtener datos JSON del DTE
     $DteTmp = new Model_DteTmp($Emisor->rut, $receptor, $dte, $codigo);
     if (!$DteTmp->exists()) {
         \sowerphp\core\Model_Datasource_Session::message('No existe el DTE temporal solicitado', 'error');
         $this->redirect('/dte/dte_tmps');
     }
     // armar xml a partir de datos del dte temporal
     $xml = $DteTmp->getEnvioDte()->generar();
     if (!$xml) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible crear el PDF para previsualización:<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect('/dte/dte_tmps');
     }
     // armar datos con archivo XML y flag para indicar si es cedible o no
     $data = ['xml' => base64_encode($xml), 'cedible' => false, 'compress' => false];
     // si hay un logo para la empresa se usa
     $logo = \sowerphp\core\Configure::read('dte.logos.dir') . '/' . $Emisor->rut . '.png';
     if (is_readable($logo)) {
         $data['logo'] = base64_encode(file_get_contents($logo));
     }
     // realizar consulta a la API
     $rest = new \sowerphp\core\Network_Http_Rest();
     $rest->setAuth($this->Auth->User ? $this->Auth->User->hash : $this->token);
     $response = $rest->post($this->request->url . '/api/dte/documentos/generar_pdf', $data);
     if ($response['status']['code'] != 200) {
         \sowerphp\core\Model_Datasource_Session::message($response['body'], 'error');
         return;
     }
     // si dió código 200 se entrega la respuesta del servicio web
     foreach (['Content-Disposition', 'Content-Length', 'Content-Type'] as $header) {
         if (isset($response['header'][$header])) {
             header($header . ': ' . $response['header'][$header]);
         }
     }
     echo $response['body'];
     exit;
 }
Ejemplo n.º 6
0
 /**
  * Método que empaqueta y comprime archivos (uno o varios, o directorios).
  * Si se pide usar formato zip entonces se usará ZipArchive de PHP para
  * comprimir
  * @param filepath Directorio (o archivo) que se desea comprimir
  * @param options Arreglo con opciones para comprmir (format, download, delete)
  * @todo Preparar datos si se pasa un arreglo
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]delaf.cl)
  * @version 2015-11-03
  */
 public static function compress($file, $options = [])
 {
     // definir opciones por defecto
     $options = array_merge(['format' => 'gz', 'delete' => false, 'download' => true, 'commands' => ['gz' => 'gzip --keep :in', 'tar.gz' => 'tar czf :in.tar.gz :in', 'tar' => 'tar cf :in.tar :in', 'bz2' => 'bzip2 --keep :in', 'tar.bz2' => 'tar cjf :in.tar.bz2 :in', 'zip' => 'zip -r :in.zip :in']], $options);
     // si el archivo no se puede leer se entrega =false
     if (!is_readable($file)) {
         \sasco\LibreDTE\Log::write(Estado::COMPRESS_ERROR_READ, Estado::get(Estado::COMPRESS_ERROR_READ));
         return false;
     }
     // si es formato gz y es directorio se cambia a tgz
     if (is_dir($file)) {
         if ($options['format'] == 'gz') {
             $options['format'] = 'tar.gz';
         } else {
             if ($options['format'] == 'bz2') {
                 $options['format'] = 'tar.bz2';
             }
         }
     }
     // obtener directorio que contiene al archivo/directorio y el nombre de este
     $filepath = $file;
     $dir = dirname($file);
     $file = basename($file);
     $file_compressed = $file . '.' . $options['format'];
     // empaquetar/comprimir directorio/archivo
     if ($options['format'] == 'zip') {
         // crear archivo zip
         $zip = new \ZipArchive();
         if ($zip->open($dir . DIRECTORY_SEPARATOR . $file . '.zip', \ZipArchive::CREATE) !== true) {
             \sasco\LibreDTE\Log::write(Estado::COMPRESS_ERROR_ZIP, Estado::get(Estado::COMPRESS_ERROR_ZIP));
             return false;
         }
         // agregar un único archivo al zip
         if (!is_dir($filepath)) {
             $zip->addFile($filepath, $file);
         } else {
             if (is_dir($filepath)) {
                 $Iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($filepath));
                 foreach ($Iterator as $f) {
                     if (!$f->isDir()) {
                         $path = $f->getPath() . DIRECTORY_SEPARATOR . $f->getFilename();
                         $zip->addFile($path, str_replace($filepath, '', $file . DIRECTORY_SEPARATOR . $path));
                     }
                 }
             }
         }
         // escribir en el sistema de archivos y cerrar archivo
         file_put_contents($dir . DIRECTORY_SEPARATOR . $file_compressed, $zip->getStream(md5($filepath)));
         $zip->close();
     } else {
         exec('cd ' . $dir . ' && ' . str_replace(':in', $file, $options['commands'][$options['format']]));
     }
     // enviar archivo
     if ($options['download']) {
         ob_clean();
         header('Content-Disposition: attachment; filename=' . $file_compressed);
         $mimetype = self::mimetype($dir . DIRECTORY_SEPARATOR . $file_compressed);
         if ($mimetype) {
             header('Content-Type: ' . $mimetype);
         }
         header('Content-Length: ' . filesize($dir . DIRECTORY_SEPARATOR . $file_compressed));
         readfile($dir . DIRECTORY_SEPARATOR . $file_compressed);
         unlink($dir . DIRECTORY_SEPARATOR . $file_compressed);
     }
     // borrar directorio o archivo que se está comprimiendo si así se ha
     // solicitado
     if ($options['delete']) {
         if (is_dir($filepath)) {
             self::rmdir($filepath);
         } else {
             unlink($filepath);
         }
     }
 }
 /**
  * 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));
 }
Ejemplo n.º 8
0
 /**
  * Método que valida el XML que se genera para la respuesta del envío
  * @return =true si el schema del documento del envío es válido, =null si no se pudo determinar
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public function schemaValidate()
 {
     if (!$this->xml_data) {
         return null;
     }
     $xsd = dirname(dirname(dirname(__FILE__))) . '/schemas/EnvioRecibos_v10.xsd';
     $this->xml = new \sasco\LibreDTE\XML();
     $this->xml->loadXML($this->xml_data);
     $result = $this->xml->schemaValidate($xsd);
     if (!$result) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIORECIBOS_ERROR_SCHEMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIORECIBOS_ERROR_SCHEMA, implode("\n", libxml_get_errors())));
     }
     return $result;
 }
Ejemplo n.º 9
0
 /**
  * Método para obtener la clave pública (certificado X.509) del SII
  *
  * \code{.php}
  *   $pub_key = \sasco\LibreDTE\Sii::cert(100); // Certificado IDK 100 (certificación)
  * \endcode
  *
  * @param idk IDK de la clave pública del SII. Si no se indica se tratará de determinar con el ambiente que se esté usando
  * @return Contenido del certificado
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-16
  */
 public static function cert($idk = null)
 {
     // si se pasó un idk y existe el archivo asociado se entrega
     if ($idk) {
         $cert = dirname(dirname(__FILE__)) . '/certs/' . $idk . '.cer';
         if (is_readable($cert)) {
             return file_get_contents($cert);
         }
     }
     // buscar certificado y entregar si existe o =false si no
     $ambiente = self::getAmbiente();
     $cert = dirname(dirname(__FILE__)) . '/certs/' . self::$config['certs'][$ambiente] . '.cer';
     if (!is_readable($cert)) {
         \sasco\LibreDTE\Log::write(Estado::SII_ERROR_CERTIFICADO, Estado::get(Estado::SII_ERROR_CERTIFICADO, self::$config['certs'][$ambiente]));
         return false;
     }
     return file_get_contents($cert);
 }
Ejemplo n.º 10
0
 /**
  * Método que permite generar un libro de guías de despacho a partir de un
  * archivo CSV con el detalle del mismo
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-12-24
  */
 public function generar_libro_guia()
 {
     // si no se viene por post terminar
     if (!isset($_POST['submit'])) {
         return;
     }
     // verificar campos no estén vacíos
     $campos = ['RutEmisorLibro', 'PeriodoTributario', 'FchResol', 'NroResol', 'TipoLibro', 'TipoEnvio', 'FolioNotificacion', 'contrasenia'];
     foreach ($campos as $campo) {
         if (!isset($_POST[$campo][0])) {
             \sowerphp\core\Model_Datasource_Session::message($campo . ' no puede estar en blanco', 'error');
             return;
         }
     }
     // si no se pasó el archivo error
     if (!isset($_FILES['archivo']) or $_FILES['archivo']['error']) {
         \sowerphp\core\Model_Datasource_Session::message('Debes enviar el archivo CSV con el detalle de las guías a la que deseas generar su XML', 'error');
         return;
     }
     // si no se pasó la firma error
     if (!isset($_FILES['firma']) or $_FILES['firma']['error']) {
         \sowerphp\core\Model_Datasource_Session::message('Debes enviar el archivo con la firma digital', 'error');
         return;
     }
     // Objeto de la Firma
     try {
         $Firma = new \sasco\LibreDTE\FirmaElectronica(['data' => file_get_contents($_FILES['firma']['tmp_name']), 'pass' => $_POST['contrasenia']]);
     } catch (\Exception $e) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible abrir la firma digital, quizás contraseña incorrecta', 'error');
         return;
     }
     // generar caratula del libro
     $caratula = ['RutEmisorLibro' => str_replace('.', '', $_POST['RutEmisorLibro']), 'PeriodoTributario' => $_POST['PeriodoTributario'], 'FchResol' => $_POST['FchResol'], 'NroResol' => $_POST['NroResol'], 'TipoLibro' => $_POST['TipoLibro'], 'TipoEnvio' => $_POST['TipoEnvio'], 'FolioNotificacion' => $_POST['FolioNotificacion']];
     // generar libro de guías
     $LibroGuia = new \sasco\LibreDTE\Sii\LibroGuia();
     $LibroGuia->agregarCSV($_FILES['archivo']['tmp_name']);
     $LibroGuia->setFirma($Firma);
     $LibroGuia->setCaratula($caratula);
     $xml = $LibroGuia->generar();
     if (!$LibroGuia->schemaValidate()) {
         \sowerphp\core\Model_Datasource_Session::message(implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         return;
     }
     // descargar XML
     $file = TMP . '/' . $LibroGuia->getID() . '.xml';
     file_put_contents($file, $xml);
     \sasco\LibreDTE\File::compress($file, ['format' => 'zip', 'delete' => true]);
     exit;
 }
Ejemplo n.º 11
0
 /**
  * Método que realiza la firma del DTE
  * @param Firma objeto que representa la Firma Electrónca
  * @return =true si el DTE pudo ser fimado o =false si no se pudo firmar
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public function firmar(\sasco\LibreDTE\FirmaElectronica $Firma)
 {
     $parent = $this->xml->getElementsByTagName($this->tipo_general)->item(0);
     $this->xml->generate(['TmstFirma' => $this->timestamp], $parent);
     $xml = $Firma->signXML($this->xml->saveXML(), '#' . $this->id, $this->tipo_general);
     if (!$xml) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::DTE_ERROR_FIRMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::DTE_ERROR_FIRMA, $this->getID()));
         return false;
     }
     $this->loadXML($xml);
     return true;
 }
Ejemplo n.º 12
0
 /**
  * Método que valida el XML que se genera para la respuesta del envío
  * @return =true si el schema del documento del envío es válido, =null si no se pudo determinar
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-18
  */
 public function schemaValidate()
 {
     if (!$this->xml_data) {
         return null;
     }
     $xsd = dirname(dirname(dirname(__FILE__))) . '/schemas/RespuestaEnvioDTE_v10.xsd';
     $this->xml = new \sasco\LibreDTE\XML();
     $this->xml->loadXML($this->xml_data);
     $result = $this->xml->schemaValidate($xsd);
     if (!$result) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::RESPUESTAENVIO_ERROR_SCHEMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::RESPUESTAENVIO_ERROR_SCHEMA, implode("\n", $this->xml->getErrors())));
     }
     return $result;
 }
Ejemplo n.º 13
0
 /**
  * Recurso de la API que permite validar el TED (timbre electrónico)
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-19
  */
 public function _api_verificar_ted_POST()
 {
     // verificar si se pasaron credenciales de un usuario
     $User = $this->Api->getAuthUser();
     if (is_string($User)) {
         $this->Api->send($User, 401);
     }
     // obtener TED
     $TED = base64_decode($this->Api->data);
     if (strpos($TED, '<?xml') !== 0) {
         $TED = '<?xml version="1.0" encoding="ISO-8859-1"?>' . "\n" . $TED;
     }
     // crear xml con el ted y obtener datos en arreglo
     $xml = new \sasco\LibreDTE\XML();
     $xml->loadXML($TED);
     $datos = $xml->toArray();
     // verificar firma del ted
     $DD = $xml->getFlattened('/TED/DD');
     $FRMT = $datos['TED']['FRMT'];
     $pub_key = \sasco\LibreDTE\FirmaElectronica::getFromModulusExponent($datos['TED']['DD']['CAF']['DA']['RSAPK']['M'], $datos['TED']['DD']['CAF']['DA']['RSAPK']['E']);
     if (openssl_verify($DD, base64_decode($FRMT), $pub_key, OPENSSL_ALGO_SHA1) !== 1) {
         $this->Api->send('Firma del timbre incorrecta', 500);
     }
     // verificar que datos del timbre correspondan con datos del CAF
     if ($datos['TED']['DD']['RE'] != $datos['TED']['DD']['CAF']['DA']['RE']) {
         $this->Api->send('RUT del timbre no corresponde con RUT del CAF', 500);
     }
     if ($datos['TED']['DD']['TD'] != $datos['TED']['DD']['CAF']['DA']['TD']) {
         $this->Api->send('Tipo de DTE del timbre no corresponde con tipo de DTE del CAF', 500);
     }
     if ($datos['TED']['DD']['F'] < $datos['TED']['DD']['CAF']['DA']['RNG']['D'] or $datos['TED']['DD']['F'] > $datos['TED']['DD']['CAF']['DA']['RNG']['H']) {
         $this->Api->send('Folio del DTE del timbre fuera del rango del CAF', 500);
     }
     // definir si se consultará en certificación o producción
     define('_LibreDTE_CERTIFICACION_', $datos['TED']['DD']['CAF']['DA']['IDK'] == 100);
     // crear objeto firma
     $Firma = new \sasco\LibreDTE\FirmaElectronica();
     // obtener token
     $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
     if (!$token) {
         return $this->Api->send(\sasco\LibreDTE\Log::readAll(), 500);
     }
     // verificar estado del DTE con el SII
     list($RutConsultante, $DvConsultante) = explode('-', $Firma->getID());
     list($RutCompania, $DvCompania) = explode('-', $datos['TED']['DD']['RE']);
     list($RutReceptor, $DvReceptor) = explode('-', $datos['TED']['DD']['RR']);
     list($a, $m, $d) = explode('-', $datos['TED']['DD']['FE']);
     $xml = \sasco\LibreDTE\Sii::request('QueryEstDte', 'getEstDte', ['RutConsultante' => $RutConsultante, 'DvConsultante' => $DvConsultante, 'RutCompania' => $RutCompania, 'DvCompania' => $DvCompania, 'RutReceptor' => $RutReceptor, 'DvReceptor' => $DvReceptor, 'TipoDte' => $datos['TED']['DD']['TD'], 'FolioDte' => $datos['TED']['DD']['F'], 'FechaEmisionDte' => $d . $m . $a, 'MontoDte' => $datos['TED']['DD']['MNT'], 'token' => $token]);
     if ($xml === false) {
         return $this->Api->send(\sasco\LibreDTE\Log::readAll(), 500);
     }
     return (array) $xml->xpath('/SII:RESPUESTA/SII:RESP_HDR')[0];
 }
Ejemplo n.º 14
0
 /**
  * Método que entrega el arreglo con los objetos DTE del envío
  * @return Arreglo de objetos DTE
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public function getDocumentos()
 {
     // si no hay documentos se deben crear
     if (!$this->dtes) {
         // si no hay XML no se pueden crear los documentos
         if (!$this->xml) {
             \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::ENVIODTE_GETDOCUMENTOS_FALTA_XML, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::ENVIODTE_GETDOCUMENTOS_FALTA_XML));
             return false;
         }
         // crear documentos a partir del XML
         $DTEs = $this->xml->getElementsByTagName('DTE');
         foreach ($DTEs as $nodo_dte) {
             $this->dtes[] = new Dte($nodo_dte->C14N(), false);
             // cargar DTE sin normalizar
         }
     }
     return $this->dtes;
 }
Ejemplo n.º 15
0
 /**
  * Método que entrega un arreglo con todos los datos de los contribuyentes
  * que operan con factura electrónica descargados desde el SII
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-30
  */
 public static function getContribuyentes(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null)
 {
     // solicitar token
     $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
     if (!$token) {
         return false;
     }
     // definir ambiente y servidor
     $ambiente = self::getAmbiente($ambiente);
     $servidor = self::$config['servidor'][$ambiente];
     // preparar consulta curl
     $curl = curl_init();
     $header = ['User-Agent: Mozilla/4.0 (compatible; PROG 1.0; Windows NT 5.0; YComp 5.0.2.4)', 'Referer: https://' . $servidor . '.sii.cl/cvc/dte/ee_empresas_dte.html', 'Cookie: TOKEN=' . $token, 'Accept-Encoding' => 'gzip, deflate, sdch'];
     $url = 'https://' . $servidor . '.sii.cl/cvc_cgi/dte/ee_consulta_empresas_dwnld?NOMBRE_ARCHIVO=ce_empresas_dwnld_' . date('Ymd') . '.csv';
     curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
     curl_setopt($curl, CURLOPT_URL, $url);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     // si no se debe verificar el SSL se asigna opción a curl, además si
     // se está en el ambiente de producción y no se verifica SSL se
     // generará un error de nivel E_USER_NOTICE
     if (!self::$verificar_ssl) {
         if ($ambiente == self::PRODUCCION) {
             $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
             trigger_error($msg, E_USER_NOTICE);
             \sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING);
         }
         curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
     }
     // realizar consulta curl
     $response = curl_exec($curl);
     if (!$response) {
         return false;
     }
     // cerrar sesión curl
     curl_close($curl);
     // entregar datos del archivo CSV
     ini_set('memory_limit', '1024M');
     $lines = explode("\n", $response);
     $n_lines = count($lines);
     $data = [];
     for ($i = 1; $i < $n_lines; $i++) {
         $row = str_getcsv($lines[$i], ';', '');
         unset($lines[$i]);
         if (!isset($row[5])) {
             continue;
         }
         for ($j = 0; $j < 6; $j++) {
             $row[$j] = trim($row[$j]);
         }
         $row[1] = utf8_decode($row[1]);
         $row[4] = strtolower($row[4]);
         $row[5] = strtolower($row[5]);
         $data[] = $row;
     }
     return $data;
 }
Ejemplo n.º 16
0
 /**
  * Acción que envía el archivo XML del libro de ventas al SII
  * Si no hay documentos en el período se enviará sin movimientos
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-12-27
  */
 public function enviar_sii($periodo)
 {
     $Emisor = $this->getContribuyente();
     // si el periodo es mayor o igual al actual no se puede enviar
     if ($periodo >= date('Ym')) {
         \sowerphp\core\Model_Datasource_Session::message('No puede enviar el libro de ventas del período ' . $periodo . ', debe esperar al mes siguiente del período', 'error');
         $this->redirect(str_replace('enviar_sii', 'ver', $this->request->request));
     }
     // obtener ventas
     $ventas = $Emisor->getVentas($periodo);
     // crear libro
     $Libro = new \sasco\LibreDTE\Sii\LibroCompraVenta();
     // 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');
     }
     // agregar detalle
     $documentos = 0;
     foreach ($ventas as $venta) {
         $documentos++;
         // armar detalle para agregar al libro
         $d = [];
         foreach ($venta as $k => $v) {
             if (strpos($k, 'impuesto_') !== 0) {
                 if ($v !== null) {
                     $d[$this->libro_cols[$k]] = $v;
                 }
             }
         }
         // agregar otros impuestos
         if (!empty($venta['impuesto_codigo'])) {
             $d['OtrosImp'] = ['CodImp' => $venta['impuesto_codigo'], 'TasaImp' => $venta['impuesto_tasa'], 'MntImp' => $venta['impuesto_monto']];
         }
         // agregar detalle al libro
         $Libro->agregar($d);
     }
     // agregar carátula al libro
     $Libro->setCaratula(['RutEmisorLibro' => $Emisor->rut . '-' . $Emisor->dv, 'RutEnvia' => $Firma->getID(), 'PeriodoTributario' => substr($periodo, 0, 4) . '-' . substr($periodo, 4), 'FchResol' => $Emisor->certificacion ? $Emisor->certificacion_resolucion : $Emisor->resolucion_fecha, 'NroResol' => $Emisor->certificacion ? 0 : $Emisor->resolucion_numero, 'TipoOperacion' => 'VENTA', 'TipoLibro' => 'MENSUAL', 'TipoEnvio' => 'TOTAL']);
     // obtener XML
     $Libro->setFirma($Firma);
     $xml = $Libro->generar();
     if (!$xml) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible generar el libro de ventas<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect(str_replace('enviar_sii', 'ver', $this->request->request));
     }
     // enviar al SII
     $track_id = $Libro->enviar();
     if (!$track_id) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible enviar el libro de ventas al SII<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect(str_replace('enviar_sii', 'ver', $this->request->request));
     }
     // guardar libro de ventas
     $DteVenta = new Model_DteVenta($Emisor->rut, $periodo, (int) $Emisor->certificacion);
     $DteVenta->documentos = $documentos;
     $DteVenta->xml = base64_encode($xml);
     $DteVenta->track_id = $track_id;
     $DteVenta->save();
     \sowerphp\core\Model_Datasource_Session::message('Libro de ventas período ' . $periodo . ' envíado', 'ok');
     $this->redirect(str_replace('enviar_sii', 'ver', $this->request->request));
 }
Ejemplo n.º 17
0
 /**
  * Método que valida el XML que se genera para la respuesta del envío
  * @return =true si el schema del documento del envío es válido, =null si no se pudo determinar
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-18
  */
 public function schemaValidate()
 {
     if (!$this->xml_data) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::LIBROCOMPRAVENTA_FALTA_XML, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::LIBROCOMPRAVENTA_FALTA_XML));
         return null;
     }
     $xsd = dirname(dirname(dirname(__FILE__))) . '/schemas/LibroCVS_v10.xsd';
     $this->xml = new \sasco\LibreDTE\XML();
     $this->xml->loadXML($this->xml_data);
     $result = $this->xml->schemaValidate($xsd);
     if (!$result) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::LIBROCOMPRAVENTA_ERROR_SCHEMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::LIBROCOMPRAVENTA_ERROR_SCHEMA, implode("\n", $this->xml->getErrors())));
     }
     return $result;
 }
Ejemplo n.º 18
0
 /**
  * Acción que solicita se envíe una nueva revisión del DTE al email
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-25
  */
 public function solicitar_revision($dte, $folio)
 {
     $Emisor = $this->getContribuyente();
     // obtener DTE emitido
     $DteEmitido = new Model_DteEmitido($Emisor->rut, $dte, $folio, (int) $Emisor->certificacion);
     if (!$DteEmitido->exists()) {
         \sowerphp\core\Model_Datasource_Session::message('No existe el DTE solicitado', 'error');
         $this->redirect('/dte/dte_emitidos/listar');
     }
     // si no tiene track id error
     if (!$DteEmitido->track_id) {
         \sowerphp\core\Model_Datasource_Session::message('DTE no tiene Track ID, primero debe enviarlo al SII', 'error');
         $this->redirect(str_replace('solicitar_revision', 'ver', $this->request->request));
     }
     // 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');
     }
     // obtener token
     $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
     if (!$token) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible obtener el token para el SII<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         $this->redirect(str_replace('solicitar_revision', 'ver', $this->request->request));
     }
     // solicitar envío de nueva revisión
     $estado = \sasco\LibreDTE\Sii::request('wsDTECorreo', 'reenvioCorreo', [$token, $Emisor->rut, $Emisor->dv, $DteEmitido->track_id]);
     if ($estado === false) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible solicitar una nueva revisión del DTE.<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
     } else {
         if ((int) $estado->xpath('/SII:RESPUESTA/SII:RESP_HDR/SII:ESTADO')[0]) {
             \sowerphp\core\Model_Datasource_Session::message('No fue posible solicitar una nueva revisión del DTE: ' . $estado->xpath('/SII:RESPUESTA/SII:RESP_HDR/SII:GLOSA')[0], 'error');
         } else {
             \sowerphp\core\Model_Datasource_Session::message('Se solicitó nueva revisión del DTE, verificar estado en unos segundos', 'ok');
         }
     }
     // redireccionar
     $this->redirect(str_replace('solicitar_revision', 'ver', $this->request->request));
 }
Ejemplo n.º 19
0
 /**
  * Método para obtener el token de la sesión a través de una semilla
  * previamente firmada
  *
  * WSDL producción: https://palena.sii.cl/DTEWS/GetTokenFromSeed.jws?WSDL
  * WSDL certificación: https://maullin.sii.cl/DTEWS/GetTokenFromSeed.jws?WSDL
  *
  * @param Firma objeto de la Firma electrónica o arreglo con configuración de la misma
  * @return Token para autenticación en SII o =false si no se pudo obtener
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public static function getToken($Firma = [])
 {
     $semilla = self::getSeed();
     if (!$semilla) {
         return false;
     }
     $requestFirmado = self::getTokenRequest($semilla, $Firma);
     if (!$requestFirmado) {
         return false;
     }
     $xml = \sasco\LibreDTE\Sii::request('GetTokenFromSeed', 'getToken', $requestFirmado);
     if ($xml === false or (string) $xml->xpath('/SII:RESPUESTA/SII:RESP_HDR/ESTADO')[0] !== '00') {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::AUTH_ERROR_TOKEN, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::AUTH_ERROR_TOKEN));
         return false;
     }
     return (string) $xml->xpath('/SII:RESPUESTA/SII:RESP_BODY/TOKEN')[0];
 }
Ejemplo n.º 20
0
 /**
  * Acción que permite subir un caf para un tipo de folio
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-22
  */
 public function subir_caf()
 {
     $Emisor = $this->getContribuyente();
     $this->set(['Emisor' => $Emisor]);
     // procesar solo si se envió el formulario
     if (isset($_POST['submit'])) {
         // verificar que se haya podido subir CAF
         if (!isset($_FILES['caf']) or $_FILES['caf']['error']) {
             \sowerphp\core\Model_Datasource_Session::message('Ocurrió un error al subir el CAF', 'error');
             return;
         }
         if (\sowerphp\general\Utility_File::mimetype($_FILES['caf']['tmp_name']) != 'application/xml') {
             \sowerphp\core\Model_Datasource_Session::message('Formato del archivo ' . $_FILES['caf']['name'] . ' es incorrecto', 'error');
             return;
         }
         // cargar caf
         $caf = file_get_contents($_FILES['caf']['tmp_name']);
         $Folios = new \sasco\LibreDTE\Sii\Folios($caf);
         // si no se pudo validar el caf error
         if (!$Folios->getTipo()) {
             \sowerphp\core\Model_Datasource_Session::message('No fue posible cargar el CAF ' . $_FILES['caf']['name'] . ':<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
             return;
         }
         // verificar que el caf tenga previamente cargado un mantenedor de folio
         $DteFolio = new Model_DteFolio($Emisor->rut, $Folios->getTipo(), (int) $Folios->getCertificacion());
         if (!$DteFolio->exists()) {
             \sowerphp\core\Model_Datasource_Session::message('Primero debe crear el mantenedor de los folios de tipo ' . $Folios->getTipo(), 'error');
             return;
         }
         // verificar que el caf sea del emisor
         if ($Folios->getEmisor() != $Emisor->rut . '-' . $Emisor->dv) {
             \sowerphp\core\Model_Datasource_Session::message('RUT del CAF ' . $Folios->getEmisor() . ' no corresponde con el RUT de la empresa ' . $Emisor->razon_social . ' ' . $Emisor->rut . '-' . $Emisor->dv, 'error');
             return;
         }
         // verificar que el folio que se está subiendo sea para el ambiente actual de la empresa
         $ambiente_empresa = $Emisor->certificacion ? 'certificación' : 'producción';
         $ambiente_caf = $Folios->getCertificacion() ? 'certificación' : 'producción';
         if ($ambiente_empresa != $ambiente_caf) {
             \sowerphp\core\Model_Datasource_Session::message('Empresa está en ambiente de ' . $ambiente_empresa . ' pero folios son de ' . $ambiente_caf, 'error');
             return;
         }
         // crear caf para el folio
         $DteCaf = new Model_DteCaf($DteFolio->emisor, $DteFolio->dte, (int) $Folios->getCertificacion(), $Folios->getDesde());
         if ($DteCaf->exists()) {
             \sowerphp\core\Model_Datasource_Session::message('El CAF para el documento de tipo ' . $DteCaf->dte . ' que inicia en ' . $Folios->getDesde() . ' en ambiente de ' . $ambiente_caf . ' ya estaba cargado', 'warning');
             return;
         }
         $DteCaf->hasta = $Folios->getHasta();
         $DteCaf->xml = \website\Dte\Utility_Data::encrypt($caf);
         try {
             $DteCaf->save();
         } catch (\sowerphp\core\Exception_Model_Datasource_Database $e) {
             \sowerphp\core\Model_Datasource_Session::message('No fue posible guardar el CAF: ' . $e->getMessage(), 'error');
             return;
         }
         // actualizar mantenedor de folios
         if (!$DteFolio->disponibles) {
             $DteFolio->siguiente = $Folios->getDesde();
             $DteFolio->disponibles = $Folios->getHasta() - $Folios->getDesde() + 1;
         } else {
             $DteFolio->disponibles += $Folios->getHasta() - $Folios->getDesde() + 1;
         }
         $DteFolio->alertado = 'f';
         try {
             $DteFolio->save();
             \sowerphp\core\Model_Datasource_Session::message('El CAF para el documento de tipo ' . $DteCaf->dte . ' que inicia en ' . $Folios->getDesde() . ' en ambiente de ' . $ambiente_caf . ' fue cargado, el siguiente folio disponible es ' . $DteFolio->siguiente, 'ok');
             $this->redirect('/dte/admin/dte_folios');
         } catch (\sowerphp\core\Exception_Model_Datasource_Database $e) {
             \sowerphp\core\Model_Datasource_Session::message('El CAF se guardó, pero no fue posible actualizar el mantenedor de folios, deberá actualizar manualmente. ' . $e->getMessage(), 'error');
             return;
         }
     }
 }
Ejemplo n.º 21
0
 /**
  * Método para generar un error usando una excepción de SowerPHP o terminar
  * el script si no se está usando el framework
  * @param msg Mensaje del error
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 private function error($msg)
 {
     $msg = Estado::get(Estado::FIRMA_ERROR, $msg);
     if (class_exists('\\sowerphp\\core\\Exception')) {
         throw new \sowerphp\core\Exception($msg);
     } else {
         \sasco\LibreDTE\Log::write(Estado::FIRMA_ERROR, $msg);
     }
     return false;
 }
Ejemplo n.º 22
0
 /**
  * Método que valida el XML del documento
  * @return =true si el schema del documento del envío es válido, =null si no se pudo determinar
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-12-14
  */
 public function schemaValidate()
 {
     if (!$this->xml_data) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::DOCUMENTO_FALTA_XML, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::DOCUMENTO_FALTA_XML, substr(get_class($this), strrpos(get_class($this), '\\') + 1)));
         return null;
     }
     $this->xml = new \sasco\LibreDTE\XML();
     $this->xml->loadXML($this->xml_data);
     $xsd = dirname(dirname(dirname(dirname(__FILE__)))) . '/schemas/' . $this->xml->getSchema();
     if (!is_readable($xsd)) {
         return null;
     }
     $result = $this->xml->schemaValidate($xsd);
     if (!$result) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::DOCUMENTO_ERROR_SCHEMA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::DOCUMENTO_ERROR_SCHEMA, substr(get_class($this), strrpos(get_class($this), '\\') + 1), implode("\n", $this->xml->getErrors())));
     }
     return $result;
 }
Ejemplo n.º 23
0
// EnvioDTE::enviar() que envía el XML que se genera, ver ejemplo 010-set_pruebas.php
$EnvioDTE = new \sasco\LibreDTE\Sii\EnvioDTE();
$EnvioDTE->agregar($DTE);
$EnvioDTE->setCaratula($caratula);
$EnvioDTE->setFirma($Firma);
$xml = $EnvioDTE->generar();
// solicitar token
$token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
if (!$token) {
    foreach (\sasco\LibreDTE\Log::readAll() as $log) {
        echo $log, "\n";
    }
    exit;
}
// enviar DTE
$result = \sasco\LibreDTE\Sii::enviar($caratula['RutEnvia'], $factura['Encabezado']['Emisor']['RUTEmisor'], $xml, $token);
// si hubo algún error al enviar al servidor mostrar
if ($result === false) {
    foreach (\sasco\LibreDTE\Log::readAll() as $log) {
        echo $log, "\n";
    }
    exit;
}
// Mostrar resultado del envío
if ($result->STATUS != '0') {
    foreach (\sasco\LibreDTE\Log::readAll() as $log) {
        echo $log, "\n";
    }
    exit;
}
echo 'DTE enviado. Track ID ' . $result->TRACKID, "\n";
Ejemplo n.º 24
0
 /**
  * Método que permite generar un libro de Compras o Ventas a partir de un
  * archivo CSV con el detalle del mismo
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-10-05
  */
 public function generar_libro()
 {
     // si no se viene por post terminar
     if (!isset($_POST['submit'])) {
         return;
     }
     // verificar campos no estén vacíos
     $campos = ['TipoOperacion', 'RutEmisorLibro', 'PeriodoTributario', 'FchResol', 'NroResol', 'TipoLibro', 'TipoEnvio', 'FolioNotificacion', 'contrasenia'];
     foreach ($campos as $campo) {
         if (empty($_POST[$campo])) {
             \sowerphp\core\Model_Datasource_Session::message($campo . ' no puede estar en blanco', 'error');
             return;
         }
     }
     // si no se pasó el archivo error
     if (!isset($_FILES['archivo']) or $_FILES['archivo']['error']) {
         \sowerphp\core\Model_Datasource_Session::message('Debes enviar el archivo CSV con el detalle de las compras o ventas al que deseas generar su XML', 'error');
         return;
     }
     // si no se pasó la firma error
     if (!isset($_FILES['firma']) or $_FILES['firma']['error']) {
         \sowerphp\core\Model_Datasource_Session::message('Debes enviar el archivo con la firma digital', 'error');
         return;
     }
     // Objeto de la Firma
     try {
         $Firma = new \sasco\LibreDTE\FirmaElectronica(['data' => file_get_contents($_FILES['firma']['tmp_name']), 'pass' => $_POST['contrasenia']]);
     } catch (\Exception $e) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible abrir la firma digital, quizás contraseña incorrecta', 'error');
         return;
     }
     // generar caratula del libro
     $caratula = ['RutEmisorLibro' => str_replace('.', '', $_POST['RutEmisorLibro']), 'RutEnvia' => $Firma->getID(), 'PeriodoTributario' => $_POST['PeriodoTributario'], 'FchResol' => $_POST['FchResol'], 'NroResol' => $_POST['NroResol'], 'TipoOperacion' => $_POST['TipoOperacion'], 'TipoLibro' => $_POST['TipoLibro'], 'TipoEnvio' => $_POST['TipoEnvio'], 'FolioNotificacion' => $_POST['FolioNotificacion']];
     // definir si es certificacion
     $caratula_certificacion = ['COMPRA' => ['PeriodoTributario' => 2000, 'FchResol' => '2006-01-20', 'NroResol' => 102006, 'TipoLibro' => 'ESPECIAL', 'TipoEnvio' => 'TOTAL', 'FolioNotificacion' => 102006], 'VENTA' => ['PeriodoTributario' => 1980, 'FchResol' => '2006-01-20', 'NroResol' => 102006, 'TipoLibro' => 'ESPECIAL', 'TipoEnvio' => 'TOTAL', 'FolioNotificacion' => 102006]];
     $certificacion = true;
     foreach ($caratula_certificacion[$caratula['TipoOperacion']] as $attr => $val) {
         if ($caratula[$attr] != $val or $attr == 'PeriodoTributario' and substr($caratula[$attr], 0, 4) != $val) {
             $certificacion = false;
             break;
         }
     }
     // generar libro de compras o venta
     $LibroCompraVenta = new \sasco\LibreDTE\Sii\LibroCompraVenta();
     if ($caratula['TipoOperacion'] === 'COMPRA') {
         $LibroCompraVenta->agregarComprasCSV($_FILES['archivo']['tmp_name']);
     } else {
         $LibroCompraVenta->agregarVentasCSV($_FILES['archivo']['tmp_name']);
     }
     $LibroCompraVenta->setCaratula($caratula);
     if (!$certificacion) {
         $LibroCompraVenta->setFirma($Firma);
     }
     $xml = $LibroCompraVenta->generar($caratula['TipoOperacion'] == 'COMPRA');
     if (!$certificacion and !$LibroCompraVenta->schemaValidate()) {
         \sowerphp\core\Model_Datasource_Session::message(implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
         return;
     }
     // descargar XML
     $file = TMP . '/' . $LibroCompraVenta->getID() . '.xml';
     file_put_contents($file, $xml);
     \sasco\LibreDTE\File::compress($file, ['format' => 'zip', 'delete' => true]);
     exit;
 }
 /**
  * Método que agrega o modifica un DTE recibido
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-28
  */
 private function save()
 {
     $Emisor = $this->getContribuyente();
     // revisar datos minimos
     foreach (['emisor', 'dte', 'folio', 'fecha', 'tasa'] as $attr) {
         if (!isset($_POST[$attr][0])) {
             \sowerphp\core\Model_Datasource_Session::message('Debe indicar ' . $attr, 'error');
             return;
         }
     }
     // crear dte recibido
     list($emisor, $dv) = explode('-', str_replace('.', '', $_POST['emisor']));
     $DteRecibido = new Model_DteRecibido($emisor, $_POST['dte'], (int) $_POST['folio'], (int) $Emisor->certificacion);
     $DteRecibido->receptor = $Emisor->rut;
     $DteRecibido->tasa = (int) $_POST['tasa'];
     $DteRecibido->fecha = $_POST['fecha'];
     $DteRecibido->exento = !empty($_POST['exento']) ? $_POST['exento'] : null;
     $DteRecibido->neto = !empty($_POST['neto']) ? $_POST['neto'] : null;
     $DteRecibido->iva = round((int) $DteRecibido->neto * ($DteRecibido->tasa / 100));
     $DteRecibido->total = (int) $DteRecibido->exento + (int) $DteRecibido->neto + $DteRecibido->iva;
     $DteRecibido->usuario = $this->Auth->User->id;
     // iva uso común, no recuperable e impuesto adicional
     $DteRecibido->iva_uso_comun = !empty($_POST['iva_uso_comun']) ? $_POST['iva_uso_comun'] : null;
     $DteRecibido->iva_no_recuperable = !empty($_POST['iva_no_recuperable']) ? $_POST['iva_no_recuperable'] : null;
     if (!empty($_POST['impuesto_adicional']) and !empty($_POST['impuesto_adicional_tasa'])) {
         $DteRecibido->impuesto_adicional = $_POST['impuesto_adicional'];
         $DteRecibido->impuesto_adicional_tasa = $_POST['impuesto_adicional_tasa'];
     } else {
         $DteRecibido->impuesto_adicional = null;
         $DteRecibido->impuesto_adicional_tasa = null;
     }
     $DteRecibido->impuesto_tipo = $_POST['impuesto_tipo'];
     $DteRecibido->anulado = isset($_POST['anulado']) ? 'A' : null;
     $DteRecibido->impuesto_sin_credito = !empty($_POST['impuesto_sin_credito']) ? $_POST['impuesto_sin_credito'] : null;
     $DteRecibido->monto_activo_fijo = !empty($_POST['monto_activo_fijo']) ? $_POST['monto_activo_fijo'] : null;
     $DteRecibido->monto_iva_activo_fijo = !empty($_POST['monto_iva_activo_fijo']) ? $_POST['monto_iva_activo_fijo'] : null;
     $DteRecibido->iva_no_retenido = !empty($_POST['iva_no_retenido']) ? $_POST['iva_no_retenido'] : null;
     // si el DTE es de producción y es electrónico entonces se consultará su
     // estado antes de poder guardar, esto evitará agregar documentos que no
     // han sido recibidos en el SII o sus datos son incorrectos
     if (!$Emisor->certificacion and $DteRecibido->getTipo()->electronico) {
         // 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');
         }
         // consultar estado dte
         $estado = $DteRecibido->getEstado($Firma);
         if ($estado === false) {
             \sowerphp\core\Model_Datasource_Session::message('No se pudo obtener el estado del DTE.<br/>' . implode('<br/>', \sasco\LibreDTE\Log::readAll()), 'error');
             return;
         } else {
             if (is_string($estado)) {
                 \sowerphp\core\Model_Datasource_Session::message('Estado DTE: ' . $estado, 'error');
                 return;
             }
         }
     }
     // todo ok con el dte así que se agrega a los dte recibidos
     try {
         $DteRecibido->save();
         \sowerphp\core\Model_Datasource_Session::message('DTE recibido guardado', 'ok');
         $this->redirect('/dte/dte_recibidos/listar');
     } catch (\sowerphp\core\Exception_Model_Datasource_Database $e) {
         \sowerphp\core\Model_Datasource_Session::message('No fue posible guardar el DTE: ' . $e->getMessage(), 'error');
     }
 }
Ejemplo n.º 26
0
/**
 * @file inc.php
 * Archivo que incluye todos los archivo .php de la biblioteca para evitar
 * incluirlos manualmente. Esto es sólo válido en los ejemplos, en código real
 * usar la autocarga de composer
 * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
 * @version 2015-12-15
 */
// activar todos los errores
ini_set('display_errors', true);
error_reporting(E_ALL);
// zona horaria
date_default_timezone_set('America/Santiago');
// incluir autocarga de composer
if (is_readable(dirname(dirname(__FILE__)) . '/vendor/autoload.php')) {
    include dirname(dirname(__FILE__)) . '/vendor/autoload.php';
} else {
    die('Para probar los ejemplos debes ejecutar primero "composer install" en el directorio ' . dirname(dirname(__FILE__)));
}
// incluir archivos de la biblioteca
/*$path = dirname(dirname(__FILE__)).'/lib';
$Iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
$files = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
foreach ($files as $file => $object) {
    include $file;
}*/
// todos los ejemplos se ejecutan con backtrace activado, esto para ayudar al
// debug de los mismos
\sasco\LibreDTE\Log::setBacktrace(true);
// incluir configuración específica de los ejemplos
include 'config.php';
 */
/**
 * @file 005-estado_envio_dte.php
 * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
 * @version 2015-09-16
 */
// respuesta en texto plano
header('Content-type: text/plain');
// incluir archivos php de la biblioteca y configuraciones
include 'inc.php';
// solicitar token
$token = \sasco\LibreDTE\Sii\Autenticacion::getToken($config['firma']);
if (!$token) {
    foreach (\sasco\LibreDTE\Log::readAll() as $error) {
        echo $error, "\n";
    }
    exit;
}
// consultar estado enviado
$rut = '';
$dv = '';
$trackID = '';
$estado = \sasco\LibreDTE\Sii::request('QueryEstUp', 'getEstUp', [$rut, $dv, $trackID, $token]);
// si el estado se pudo recuperar se muestra estado y glosa
if ($estado !== false) {
    print_r(['codigo' => (string) $estado->xpath('/SII:RESPUESTA/SII:RESP_HDR/ESTADO')[0], 'glosa' => (string) $estado->xpath('/SII:RESPUESTA/SII:RESP_HDR/GLOSA')[0]]);
}
// mostrar error si hubo
foreach (\sasco\LibreDTE\Log::readAll() as $error) {
    echo $error, "\n";
}
Ejemplo n.º 28
0
 /**
  * Método que genera el XML para el envío de la respuesta al SII
  * @param caratula Arreglo con la carátula de la respuesta
  * @param Firma Objeto con la firma electrónica
  * @return XML con la respuesta firmada o =false si no se pudo generar o firmar la respuesta
  * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
  * @version 2015-09-17
  */
 public function generar()
 {
     // si ya se había generado se entrega directamente
     if ($this->xml_data) {
         return $this->xml_data;
     }
     // si no hay respuestas para generar entregar falso
     if (!isset($this->respuesta_envios[0]) and !isset($this->respuesta_documentos[0])) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::RESPUESTAENVIO_FALTA_RESPUESTA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::RESPUESTAENVIO_FALTA_RESPUESTA));
         return false;
     }
     // si no hay carátula error
     if (!$this->caratula) {
         \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::RESPUESTAENVIO_FALTA_CARATULA, \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::RESPUESTAENVIO_FALTA_CARATULA));
         return false;
     }
     // crear arreglo de lo que se enviará
     $arreglo = ['RespuestaDTE' => ['@attributes' => ['xmlns' => 'http://www.sii.cl/SiiDte', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://www.sii.cl/SiiDte RespuestaEnvioDTE_v10.xsd', 'version' => '1.0'], 'Resultado' => ['@attributes' => ['ID' => 'ResultadoEnvio'], 'Caratula' => $this->caratula]]];
     if (isset($this->respuesta_envios[0])) {
         $arreglo['RespuestaDTE']['Resultado']['RecepcionEnvio'] = $this->respuesta_envios;
     } else {
         $arreglo['RespuestaDTE']['Resultado']['ResultadoDTE'] = $this->respuesta_documentos;
     }
     // generar XML del envío
     $xmlEnvio = (new \sasco\LibreDTE\XML())->generate($arreglo)->saveXML();
     // firmar XML del envío y entregar
     $this->xml_data = $this->Firma ? $this->Firma->signXML($xmlEnvio, '#ResultadoEnvio', 'Resultado', true) : $xmlEnvio;
     return $this->xml_data;
 }