diff --git a/api/contrib/clave/clave.php b/api/contrib/clave/clave.php
index c9b34802..3a1daa58 100644
--- a/api/contrib/clave/clave.php
+++ b/api/contrib/clave/clave.php
@@ -44,7 +44,6 @@
*/
function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacion = "", $codigoPais = "", $consecutivo = "", $codigoSeguridad = "")
{
- $tipoDocumento = params_get('tipoDocumento');
$tipoCedula = params_get('tipoCedula');
$cedula = params_get('cedula');
$situacion = params_get('situacion');
@@ -54,9 +53,8 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
$sucursal = params_get("sucursal", "001");
$terminal = params_get("terminal", "00001");
- $dia = date('d');
- $mes = date('m');
- $ano = date('y');
+ $date = new DateTime('now', new DateTimeZone('America/Costa_Rica'));
+ $fechaClave = $date->format('dmy');
// Validamos el parametro de cedula
if (!ctype_digit($cedula))
@@ -85,7 +83,7 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
}
if (!ctype_digit($consecutivo)) {
- return "El parametro consecutivo no es numeral";
+ return "El parametro consecutivo [$consecutivo] no es numeral";
} else if (strlen($consecutivo) < 10) {
$consecutivo = str_pad($consecutivo, 10, "0", STR_PAD_LEFT);
} else if (strlen($consecutivo) > 10) {
@@ -187,7 +185,7 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
}
// Crea la clave
- $clave = $codigoPais . $dia . $mes . $ano . $identificacion . $consecutivoFinal . $codSituacion . $codigoSeguridad;
+ $clave = $codigoPais . $fechaClave . $identificacion . $consecutivoFinal . $codSituacion . $codigoSeguridad;
$arrayResp = array(
"clave" => "$clave",
"consecutivo" => "$consecutivoFinal",
diff --git a/api/contrib/consultar/README.md b/api/contrib/consultar/README.md
index 6c4d165c..3be30168 100644
--- a/api/contrib/consultar/README.md
+++ b/api/contrib/consultar/README.md
@@ -1,7 +1,8 @@
-# Modulo para hacer comprobar estado de los comprobantes
+# Modulo para hacer comprobar estado de los comprobantes y su estado de recepcion
+
Se envia:
* `w` : consultar
-* `r` : consultarCom
+* `r` : recepcion o comprobante
* `clave` : Clave numerica del comprobante
-* `token`: Token funcional para enviar en el header
+* `token`: Token obtenido de Hacienda
diff --git a/api/contrib/consultar/consultar.php b/api/contrib/consultar/consultar.php
index a4f9299c..39e6225d 100644
--- a/api/contrib/consultar/consultar.php
+++ b/api/contrib/consultar/consultar.php
@@ -16,10 +16,10 @@
* along with this program. If not, see .
*/
-function consutar()
+function consultarRecepcion()
{
- $curl = curl_init();
- $clave = params_get('clave');
+ $curl = curl_init();
+ $clave = params_get('clave');
if ($clave == "" || strlen($clave) == 0)
return "La clave no puede ser en blanco";
@@ -31,45 +31,179 @@ function consutar()
$url = "https://api.comprobanteselectronicos.go.cr/recepcion/v1/recepcion/";
if ($url == null)
- return "Ha ocurrido un error en el client_id.";
-
- curl_setopt_array($curl, array(
- CURLOPT_URL => $url . $clave,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_ENCODING => "",
- CURLOPT_MAXREDIRS => 10,
- CURLOPT_SSL_VERIFYHOST => 0,
- CURLOPT_SSL_VERIFYPEER => 0,
- CURLOPT_TIMEOUT => 30,
- CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
- CURLOPT_CUSTOMREQUEST => "GET",
- CURLOPT_HTTPHEADER => array(
+ return "El client_id proporcionado (" . params_get("client_id") . ") no es válido. ";
+
+ $args = array(
+ CURLOPT_URL => $url . $clave,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_ENCODING => "",
+ CURLOPT_MAXREDIRS => 10,
+ CURLOPT_SSL_VERIFYHOST => 0,
+ CURLOPT_SSL_VERIFYPEER => 0,
+ CURLOPT_TIMEOUT => 30,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => "GET",
+ CURLOPT_HTTPHEADER => array(
"Authorization: Bearer " . params_get('token'),
"Cache-Control: no-cache",
- "Content-Type: application/x-www-form-urlencoded",
- "Postman-Token: bf8dc171-5bb7-fa54-7416-56c5cda9bf5c"
+ "Content-Type: application/x-www-form-urlencoded"
),
- ));
+ );
+
+ curl_setopt_array($curl, $args);
+ $response = curl_exec($curl);
+
+ $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+ $headers = substr($response, 0, $header_size);
+ $body = substr($response, $header_size);
- $response = curl_exec($curl);
- $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- $err = curl_error($curl);
+ $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ $err = curl_error($curl);
curl_close($curl);
- if ($err)
- {
+ if ($err) {
$arrayResp = array(
"Status" => $status,
- "to" => $apiTo,
+ "to" => $url,
"text" => $err
);
+
return $arrayResp;
}
- else
- {
- $response = json_decode($response);
- return $response;
+
+ $responseObj = json_decode($body);
+
+ if (is_object($responseObj)) {
+ // si hay respuesta de hacienda, procesar detalle de mensaje
+ if (isset($responseObj->{'respuesta-xml'})) {
+
+ $xmlRespuesta = base64_decode($responseObj->{'respuesta-xml'});
+ $xmlRespuesta = preg_replace('/>\s+', '><', $xmlRespuesta);
+ $startLength = strlen("tiene los siguientes errores: ") + 7;
+ $startPos = strpos($xmlRespuesta, "tiene los siguientes errores: ", 0);
+ if (!$startPos) {
+ // si no hay errores, retornar la respuesta normal
+ return $responseObj;
+ }
+ $endPos = strpos($xmlRespuesta, "", $startPos);
+ $detalleMensaje = substr($xmlRespuesta, $startPos + $startLength, $endPos - ($startPos + $startLength + 1));
+ $separadorMensaje = "
\n";
+ $responseObj->{'DetalleMensaje'} = explode($separadorMensaje, trim($detalleMensaje, $separadorMensaje));
+ }
+
+ return array(
+ "Status" => $status,
+ "to" => $url,
+ "text" => $responseObj
+ );
+ }
+
+ if (empty($body)) {
+ $header_lines = explode("\r\n", trim($headers));
+ $header_array = [];
+ foreach ($header_lines as $line) {
+ if (strpos($line, ':') !== false) {
+ list($key, $value) = explode(': ', $line, 2);
+ $header_array[$key] = $value;
+ }
+ }
+ return array(
+ "Status" => $header_array['x-http-status'],
+ "to" => $url,
+ "text" => $header_array['x-error-cause']
+ );
+ }
+
+ return array(
+ "Status" => $status,
+ "to" => $url,
+ "text" => $body
+ );
+}
+
+
+function consultarComprobante()
+{
+ $curl = curl_init();
+ $clave = params_get('clave');
+
+ if ($clave == "" || strlen($clave) == 0) {
+ return "La clave no puede ser en blanco";
+ }
+
+ $url = null;
+ if (params_get("client_id") == 'api-stag')
+ $url = "https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/comprobantes/";
+ else if (params_get("client_id") == 'api-prod')
+ $url = "https://api.comprobanteselectronicos.go.cr/recepcion/v1/comprobantes/";
+
+ if ($url == null) {
+ return "El client_id proporcionado (" . params_get("client_id") . ") no es válido. ";
+ }
+
+ $args = array(
+ CURLOPT_URL => $url . $clave,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_ENCODING => "",
+ CURLOPT_MAXREDIRS => 10,
+ CURLOPT_SSL_VERIFYHOST => 0,
+ CURLOPT_SSL_VERIFYPEER => 0,
+ CURLOPT_TIMEOUT => 30,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => "GET",
+ CURLOPT_HTTPHEADER => array(
+ "Authorization: Bearer " . params_get('token')
+ ),
+ );
+
+ curl_setopt_array($curl, $args);
+ $response = curl_exec($curl);
+
+ $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+ $headers = substr($response, 0, $header_size);
+ $body = substr($response, $header_size);
+
+ $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ $err = curl_error($curl);
+ curl_close($curl);
+
+ if ($err) {
+ $arrayResp = array(
+ "Status" => $status,
+ "to" => $url,
+ "text" => $err
+ );
+
+ return tools_reply($arrayResp, true);
+
}
+
+ if (empty($body)) {
+ $header_lines = explode("\r\n", trim($headers));
+ $header_array = [];
+ foreach ($header_lines as $line) {
+ if (strpos($line, ':') !== false) {
+ list($key, $value) = explode(': ', $line, 2);
+ $header_array[$key] = $value;
+ }
+ }
+ return tools_reply(array(
+ "Status" => $header_array['x-http-status'] ?? $status,
+ "to" => $url,
+ "text" => $header_array['x-error-cause']
+ ), true);
+ }
+
+ $responseObj = json_decode($body);
+
+ return tools_reply(array(
+ "Status" => $status,
+ "to" => $url,
+ "text" => $body
+ ), true);
+
}
-?>
+?>
\ No newline at end of file
diff --git a/api/contrib/consultar/module.php b/api/contrib/consultar/module.php
index 0a86b3e6..4e77ca39 100644
--- a/api/contrib/consultar/module.php
+++ b/api/contrib/consultar/module.php
@@ -31,8 +31,20 @@ function consultar_init()
{
$paths = array(
array(
- 'r' => 'consultarCom',
- 'action' => 'consutar',
+ 'r' => 'recepcion',
+ 'action' => 'consultarRecepcion',
+ 'access' => 'users_openAccess',
+ 'access_params' => 'accessName',
+ 'params' => array(
+ array("key" => "clave", "def" => "", "req" => true),
+ array("key" => "token", "def" => "", "req" => true),
+ array("key" => "client_id", "def" => "", "req" => true)
+ ),
+ 'file' => 'consultar.php'
+ ),
+ array(
+ 'r' => 'comprobante',
+ 'action' => 'consultarComprobante',
'access' => 'users_openAccess',
'access_params' => 'accessName',
'params' => array(
diff --git a/api/contrib/crlibreall/module.php b/api/contrib/crlibreall/module.php
index f94790df..bcd78fe6 100644
--- a/api/contrib/crlibreall/module.php
+++ b/api/contrib/crlibreall/module.php
@@ -110,7 +110,7 @@ function crlibreall_init()
array("key" => "total_comprobante", "def" => "", "req" => true),
array("key" => "otros", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
diff --git a/api/contrib/facturador/companny_user.php b/api/contrib/facturador/companny_user.php
index ff9996c3..790086e3 100644
--- a/api/contrib/facturador/companny_user.php
+++ b/api/contrib/facturador/companny_user.php
@@ -134,7 +134,7 @@ function companny_users_generateSessionKey($idUser, $idMasterUser) {
$q = sprintf("delete from " . db_escape($idMasterUser) . "_master_sessions where idUser='" . db_escape($idUser) . "'");
db_query($q, 0);
- $sessionKey = password_hash(time() * rand(0, 1000), PASSWORD_DEFAULT);
+ $sessionKey = password_hash(time() * rand(0, 1000), PASSWORD_ARGON2ID);
$q = sprintf("INSERT INTO " . db_escape($idMasterUser) . "_master_sessions (idUser, sessionKey, ip, lastAccess) "
. "VALUES('%s', '%s', '%s', '%s')", db_escape($idUser), db_escape($sessionKey), db_escape($_SERVER['REMOTE_ADDR']), time());
@@ -409,7 +409,7 @@ function companny_users_getMyDetails() {
* Helper function to actually create a new user and register it in the db
*/
function _companny_users_register($compannyUserDets, $idMasterUser) {
- $pwd = password_hash($compannyUserDets['pwd'], PASSWORD_DEFAULT);
+ $pwd = password_hash($compannyUserDets['pwd'], PASSWORD_ARGON2ID);
$q = sprintf("INSERT INTO " . db_escape($idMasterUser) . "_master_users (idMasterUser,fullName, userName, email, about, country, status, timestamp, lastAccess, pwd, avatar,settings)
VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", db_escape($idMasterUser), db_escape($compannyUserDets['fullName']), db_escape($compannyUserDets['userName']), db_escape($compannyUserDets['email']), db_escape(addslashes($compannyUserDets['about'])), db_escape($compannyUserDets['country']), db_escape($compannyUserDets['status']), db_escape($compannyUserDets['timestamp']), db_escape($compannyUserDets['lastAccess']), db_escape($pwd), db_escape($compannyUserDets['avatar']), db_escape($compannyUserDets['settings'])
);
@@ -511,7 +511,7 @@ function companny_users_recoverPwd() {
$temp = rand(0, 1000) + time();
- $compannyUser->pwd = password_hash($temp, PASSWORD_DEFAULT);
+ $compannyUser->pwd = password_hash($temp, PASSWORD_ARGON2ID);
grace_debug("New tmp pwd: " . $compannyUser->pwd);
# Update account
diff --git a/api/contrib/firmarXML/hacienda/firmador.php b/api/contrib/firmarXML/hacienda/firmador.php
index 6b0b3107..daab6146 100644
--- a/api/contrib/firmarXML/hacienda/firmador.php
+++ b/api/contrib/firmarXML/hacienda/firmador.php
@@ -1,7 +1,7 @@
*/
-class Firmador {
+class Firmador
+{
const FROM_XML_STRING = 1;
const FROM_XML_FILE = 2;
@@ -46,7 +47,8 @@ class Firmador {
const TO_XML_STRING = 4;
const TO_XML_FILE = 5;
- public function firmarXml($pfx,$pin,$input,$output,$path=null){
+ public function firmarXml($pfx, $pin, $input, $output, $path = null)
+ {
// return dirname(__FILE__,2) .'/xmlseclibs/xmlseclibs.php';
// Cargar un nuevo XML para ser firmado
$xml = new \DOMDocument();
@@ -59,7 +61,7 @@ public function firmarXml($pfx,$pin,$input,$output,$path=null){
// Intentar parsear el input como archivo xml. Caso contrario se detiene el script
try {
$xml->loadXML($input);
- } catch (\Exception $ex){
+ } catch (\Exception $ex) {
die($ex->getMessage());
}
@@ -72,8 +74,20 @@ public function firmarXml($pfx,$pin,$input,$output,$path=null){
// Establecer política de firma
$objSec->setSignPolicy();
+ if (empty($pfx) || !is_readable($pfx)) {
+ $message = 'No se puede acceder al certificado proporcionado.';
+ error_log('[Firmador] ' . $message . ' Ruta: ' . $pfx);
+ throw new \RuntimeException($message);
+ }
+
// Cargar la información del certificado desde el archivo *.p12
- $certInfo = $objSec->loadCertInfo($pfx,$pin);
+ $certInfo = $objSec->loadCertInfo($pfx, $pin);
+
+ if (empty($certInfo) || empty($certInfo["privateKey"])) {
+ $message = 'Certificado inválido o PIN incorrecto.';
+ error_log('[Firmador] ' . $message . ' Ruta: ' . $pfx);
+ throw new \RuntimeException($message);
+ }
// Usar la canonicalización exclusiva de c14n.
$objSec->setCanonicalMethod($objSec::C14N);
@@ -91,13 +105,13 @@ public function firmarXml($pfx,$pin,$input,$output,$path=null){
// Firmar utilizando SHA-256
// Referencia del documento
- $objSec->addReference($xml,$objSec::SHA256, [ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' ], [ 'id_ref' => $objSec->reference0Id, 'force_uri' => true ]);
+ $objSec->addReference($xml, $objSec::SHA256, ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'], ['id_ref' => $objSec->reference0Id, 'force_uri' => true]);
// Referencia de nodo de información clave
- $objSec->addReference($objSec->getKeyInfoNode(),$objSec::SHA256,null, [ 'id_ref' => $objSec->reference1Id, 'force_uri' => false, 'overwrite' => false ]);
+ $objSec->addReference($objSec->getKeyInfoNode(), $objSec::SHA256, null, ['id_ref' => $objSec->reference1Id, 'force_uri' => false, 'overwrite' => false]);
// Referencia del nodo Xades
- $objSec->addReference($objSec->getXadesNode(),$objSec::SHA256,null, [ 'force_uri' => false, 'overwrite' => false, "type" => "http://uri.etsi.org/01903#SignedProperties" ], [ [ 'qualifiedName' => 'xmlns:xades', 'value' => $objSec::XADES ] ]);
+ $objSec->addReference($objSec->getXadesNode(), $objSec::SHA256, null, ['force_uri' => false, 'overwrite' => false, "type" => "http://uri.etsi.org/01903#SignedProperties"], [['qualifiedName' => 'xmlns:xades', 'value' => $objSec::XADES]]);
// Firma el archivo xml
$objSec->sign($objKey);
@@ -105,13 +119,13 @@ public function firmarXml($pfx,$pin,$input,$output,$path=null){
// Adjuntar la firma al xml
$objSec->appendSignature($xml->documentElement);
- if ($output == self::TO_BASE64_STRING){
+ if ($output == self::TO_BASE64_STRING) {
// Devuelve el string del archivo xml firmado en formato Base64
return base64_encode($xml->saveXML());
- } else if ($output == self::TO_XML_STRING){
+ } else if ($output == self::TO_XML_STRING) {
// Devuelve el archivo xml firmado en formato string Xml
return $xml->saveXML();
- } else if ($output == self::TO_XML_FILE){
+ } else if ($output == self::TO_XML_FILE) {
// Guarda el xml firmado en la ruta especificada y devuelve el resultado
if (!is_null($path)) {
return $xml->save($path);
diff --git a/api/contrib/firmarXML/xmlseclibs/src/XMLSecurityDSig.php b/api/contrib/firmarXML/xmlseclibs/src/XMLSecurityDSig.php
index 4d8d0a45..51d4d84e 100644
--- a/api/contrib/firmarXML/xmlseclibs/src/XMLSecurityDSig.php
+++ b/api/contrib/firmarXML/xmlseclibs/src/XMLSecurityDSig.php
@@ -283,6 +283,13 @@ public function setCanonicalMethod($method)
public function setSignPolicy(){
$xmlns = $this->xmlFirstChild->getAttribute('xmlns');
switch ($xmlns){
+ case (strpos($xmlns, 'v4.3') !== false):
+ $this->signPolicy = [
+ "name" => "",
+ "url" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/Resoluci%C3%B3n_General_sobre_disposiciones_t%C3%A9cnicas_comprobantes_electr%C3%B3nicos_para_efectos_tributarios.pdf",
+ "digest" => "DWxin1xWOeI8OuWQXazh4VjLWAaCLAA954em7DMh0h8=" // Base64_Encode(Hash_File(SHA_256))
+ ];
+ break;
case (strpos($xmlns, 'v4.4') !== false):
$this->signPolicy = [
"name" => "",
diff --git a/api/contrib/genXML/genXML.php b/api/contrib/genXML/genXML.php
index b197e2c0..045420b5 100644
--- a/api/contrib/genXML/genXML.php
+++ b/api/contrib/genXML/genXML.php
@@ -19,8 +19,13 @@
/* * ************************************************** */
/* Constantes de validacion */
/* * ************************************************** */
+
+if (!defined('DECIMALES')) {
+ define('DECIMALES', 5);
+}
define("TIPODOCREFVALUES", array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '99'));
define('CODIDOREFVALUES', array('01', '02', '04', '05', '06', '07', '08', '09', '10', '11', '12', '99'));
+
const CODIGOACTIVIDADSIZE = 6;
const EMISORNOMBREMAXSIZE = 100;
const EMISORNUMEROTELMIN = 8;
@@ -30,17 +35,358 @@
const RECEPTOROTRASSENASEXTRANJEROMAXSIZE = 300;
const EMAIL_REGEX = "/^\s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*$/";
+const AUTO_CALCULAR = ["totalServGravados", "totalServExentos", "totalServExonerado", "totalServNoSujeto", "totalMercanciasGravadas", "totalMercanciasExentas", "totalMercExonerada", "totalMercNoSujeta", "totalGravado", "totalExento", "totalExonerado", "totalNoSujeto", "totalVenta", "totalDescuentos", "totalVentaNeta", "totalDesgloseImpuesto", "totalImpuesto"];
+
+tools_useTool('ValidadorXML.php');
+
+/**
+ * Valida las reglas conocidas de una linea del detalle para evitar rechazos
+ *
+ * @param int $numLinea
+ * @param object $detallesLinea
+ * @return void
+ */
+function validarLinea($numLinea, $d)
+{
+ $d = (array) $d;
+
+ if (isset($d['baseImponible']) && !isset($d['impuesto'])) {
+ tools_reply([
+ "Status" => 400,
+ "text" => "Detalle de líneas $numLinea: cuando se envía el campo 'baseImponible' es obligatorio enviar también el campo 'impuesto'",
+ ], true);
+ }
+}
+
+
+/**
+ * Obtiene el código de institución a partir del nombre
+ *
+ * @param string $nombreInstitucion Nombre de la institución
+ * @return string Código de la institución (01-12, 99) o '99' si no se encuentra
+ */
+function obtenerCodigoInstitucion($nombreInstitucion)
+{
+ $mapa = getMapaInstituciones();
+
+ // Normalizar: minúsculas, sin espacios, sin tildes
+ $nombreNormalizado = strtolower($nombreInstitucion);
+ $nombreNormalizado = str_replace(' ', '', $nombreNormalizado);
+
+ // Eliminar tildes
+ $nombreNormalizado = str_replace(
+ ['á', 'é', 'í', 'ó', 'ú', 'ñ'],
+ ['a', 'e', 'i', 'o', 'u', 'n'],
+ $nombreNormalizado
+ );
+
+ // Buscar en el mapa
+ if (isset($mapa[$nombreNormalizado])) {
+ return $mapa[$nombreNormalizado];
+ }
+
+ // Si no se encuentra, retornar '99' (Otros)
+ error_log("Institución no encontrada en catálogo: {$nombreInstitucion}. Usando código '99' (Otros)");
+ return '99';
+}
+
+/**
+ * Mapea los datos del API de exoneraciones de Hacienda al formato requerido por genXML
+ *
+ * @param array $datosAPI Datos recibidos del API de Hacienda
+ * @return array|false Datos mapeados al formato de ExoneracionType o false si hay error
+ */
+function mapearExoneracionHacienda($datosAPI)
+{
+ if (!$datosAPI || !is_array($datosAPI)) {
+ error_log('Datos de exoneración inválidos para mapear');
+ return false;
+ }
+
+ // Validar campos obligatorios del API
+ $camposRequeridos = ['numeroDocumento', 'tipoDocumento', 'nombreInstitucion', 'fechaEmision', 'porcentajeExoneracion'];
+ foreach ($camposRequeridos as $campo) {
+ if (!isset($datosAPI[$campo])) {
+ error_log("Campo obligatorio '{$campo}' no encontrado en respuesta del API de exoneraciones");
+ return false;
+ }
+ }
+
+ // Obtener código de institución a partir del nombre
+ $codigoInstitucion = obtenerCodigoInstitucion($datosAPI['nombreInstitucion']);
+
+ $fechaEmision = $datosAPI['fechaEmision'];
+
+ // Construir el objeto de exoneración mapeado
+ $exoneracion = [
+ 'tipoDocumento' => $datosAPI['tipoDocumento']['codigo'], // Código del tipo de documento (ej: "04")
+ 'numeroDocumento' => $datosAPI['numeroDocumento'], // Número de autorización (ej: "AL-00028291-24")
+ 'nombreInstitucion' => $codigoInstitucion, // Código de institución (01-12, 99)
+ 'fechaEmision' => $fechaEmision,
+ 'tarifaExoneracion' => floatval($datosAPI['porcentajeExoneracion']), // Porcentaje (ej: 13.00)
+ ];
+
+ // Agregar número de autorización como artículo (opcional pero útil)
+ if (isset($datosAPI['autorizacion'])) {
+ $exoneracion['numeroArticulo'] = strval($datosAPI['autorizacion']);
+ }
+
+ // Si el tipo de documento es "99" (Otro), agregar descripción
+ if ($datosAPI['tipoDocumento']['codigo'] === '99' && isset($datosAPI['tipoAutorizacion'])) {
+ $exoneracion['tipoDocumentoOtro'] = $datosAPI['tipoAutorizacion'];
+ }
+
+ // Si la institución es "99" (Otros), agregar nombre completo
+ if ($codigoInstitucion === '99') {
+ $exoneracion['nombreInstitucionOtros'] = $datosAPI['nombreInstitucion'];
+ }
+
+ return $exoneracion;
+}
+
+/**
+ * Consulta el API de Hacienda para validar y obtener datos de una exoneración
+ * Retorna los datos mapeados listos para usar en el XML
+ *
+ * @param string $numeroDocumento Número del documento de exoneración (ej: "AL-00028291-24")
+ * @return array|false Array con datos de exoneración mapeados o false si hay error
+ */
+function consultarYMapearExoneracion($numeroDocumento)
+{
+ // Consultar el API de Hacienda
+ $datosAPI = consultarExoneracionHacienda($numeroDocumento);
+
+ if ($datosAPI === false) {
+ return false;
+ }
+
+ // Mapear los datos al formato requerido
+ $exoneracion = mapearExoneracionHacienda($datosAPI);
+ return $exoneracion;
+}
+
+/**
+ * Mapa de instituciones (nombre normalizado => código)
+ * Los nombres están en minúsculas y sin espacios para facilitar la comparación
+ */
+function getMapaInstituciones()
+{
+ return [
+ 'ministeriodehacienda' => '01',
+ 'ministeriodelaciones exterioresyculto' => '02',
+ 'ministeriodeagriculturayganadería' => '03',
+ 'ministeriodeagriculturayganaderia' => '03', // Sin tilde
+ 'ministerioeconomíaindustriaycomercio' => '04',
+ 'ministerioeconomiaindustriaycomercio' => '04', // Sin tilde
+ 'cruzrojacostarricense' => '05',
+ 'beneméritocoerpodebomberosdecostarica' => '06',
+ 'beneméritocuerpodebomberosdecostarica' => '06',
+ 'benemeritocoerpodebomberosdecostarica' => '06', // Sin tilde
+ 'asociaciónobrasdelespíritusanto' => '07',
+ 'asociacionobrasdelespiritusanto' => '07', // Sin tilde
+ 'federacióncruzadanacionaldeprotecciónalanciano(fecrunapa)' => '08',
+ 'federacioncruzadanacionaldeproteccionalanciano(fecrunapa)' => '08', // Sin tilde
+ 'federacioncruzadanacionaldeproteccionalancianofecrunapa' => '08',
+ 'fecrunapa' => '08',
+ 'escueladeagriculturadelaregiónhúmeda(earth)' => '09',
+ 'escueladeagriculturadelaregionhumeda(earth)' => '09', // Sin tilde
+ 'escueladeagriculturadelaregionhumeda' => '09',
+ 'earth' => '09',
+ 'institutocentroamericanodeadministracióndeempresas(incae)' => '10',
+ 'institutocentroamericanodeadministraciondeempresas(incae)' => '10', // Sin tilde
+ 'institutocentroamericanodeadministraciondeempresas' => '10',
+ 'incae' => '10',
+ 'juntadeprotecciónsocial(jps)' => '11',
+ 'juntadeproteccionsocial(jps)' => '11', // Sin tilde
+ 'juntadeproteccionsocial' => '11',
+ 'jps' => '11',
+ 'autoridadreguladoradelosserviciospúblicos(aresep)' => '12',
+ 'autoridadreguladoradelosserviciospublicos(aresep)' => '12', // Sin tilde
+ 'autoridadreguladoradelosserviciospublicos' => '12',
+ 'aresep' => '12',
+ 'otros' => '99'
+ ];
+}
+
+/**
+ * Consulta la API de Hacienda para validar una exoneración
+ *
+ * @param string $numeroDocumento Número del documento de exoneración
+ * @return array|false Array con la estructura completa de la exoneración o false si hay error
+ *
+ * Estructura de respuesta esperada:
+ * - numeroDocumento: string (ej: "AL-00028291-24")
+ * - identificacion: string (ej: "2300042155")
+ * - codigoProyectoCFIA: int
+ * - porcentajeExoneracion: int (ej: 13)
+ * - autorizacion: int (ej: 28291)
+ * - fechaEmision: string (ISO 8601 format)
+ * - fechaVencimiento: string (ISO 8601 format)
+ * - ano: int (ej: 2024)
+ * - cabys: array de strings con códigos CABYS
+ * - tipoAutorizacion: string (ej: "exoneracion")
+ * - tipoDocumento: array con 'codigo' y 'descripcion'
+ * - CodigoInstitucion: string (ej: "01")
+ * - nombreInstitucion: string
+ * - poseeCabys: bool
+ */
+function consultarExoneracionHacienda($numeroDocumento)
+{
+ // Convertir a minúsculas según requerimiento
+ $numeroDocumento = strtolower($numeroDocumento);
+
+ // URL de la API de Hacienda
+ $url = 'https://api.hacienda.go.cr/fe/ex?autorizacion=' . urlencode($numeroDocumento);
+
+ // Inicializar cURL
+ $ch = curl_init();
+
+ // Configurar opciones de cURL
+ curl_setopt_array($ch, [
+ CURLOPT_URL => $url,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_TIMEOUT => 10,
+ CURLOPT_SSL_VERIFYPEER => true,
+ CURLOPT_HTTPHEADER => [
+ 'Accept: application/json',
+ 'User-Agent: Factel-API/1.0'
+ ]
+ ]);
+
+ // Ejecutar la petición
+ $response = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $error = curl_error($ch);
+
+ curl_close($ch);
+
+ // Verificar si hubo error en la petición
+ if ($error) {
+ error_log("Error consultando exoneración en Hacienda: " . $error);
+ return false;
+ }
+
+ // Verificar código HTTP
+ if ($httpCode !== 200) {
+ error_log("Error HTTP " . $httpCode . " consultando exoneración: " . $numeroDocumento);
+ return false;
+ }
+
+ // Decodificar respuesta JSON
+ $data = json_decode($response, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ error_log("Error decodificando JSON de exoneración: " . json_last_error_msg());
+ return false;
+ }
+
+ // Validar que la respuesta contenga los campos esenciales
+ if (!isset($data['numeroDocumento']) || !isset($data['porcentajeExoneracion'])) {
+ error_log("Respuesta de API de Hacienda no contiene campos esperados para: " . $numeroDocumento);
+ return false;
+ }
+
+ return $data;
+}
+
+/**
+ * Actualiza los campos auto calculables usando los detalles de la linea
+ *
+ * @param int $numLinea
+ * @param object $detallesLinea
+ * @param array &$calculados
+ * @return void
+ */
+function autoCalcularLinea($numLinea, &$d, $totalDescuentos, &$calculados = [])
+{
+
+ if (!isset($calculados['totalDescuentos'])) {
+ $calculados['totalDescuentos'] = 0;
+ }
+ $calculados['totalDescuentos'] += round(floatval($totalDescuentos), DECIMALES);
+
+ if (!isset($calculados[$numLinea])) {
+ $calculados[$numLinea] = [];
+ }
+
+ // Se obtiene de la resta del campo monto total menos monto de descuento concedido
+ $calculados[$numLinea]['montoTotal'] = round($d->precioUnitario * $d->cantidad, DECIMALES);
+ $calculados[$numLinea]['subTotal'] = round($calculados[$numLinea]['montoTotal'] - $totalDescuentos, DECIMALES);
+
+ /*
+ @TODO base imponible puede incluir
+ Este campo será de condición obligatoria, cuando el producto/ servicio este gravado con algún impuesto.
+ Se obtiene de la suma entre el campo "Subtotal", más
+ el impuesto selectivo de consumo (02),
+ el Impuesto específico de Bebidas Alcohólicas (04),
+ el Impuesto Específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador (05) y
+ el impuesto al cemento (12), cuando corresponda.
+ Este campo se podrá editar cuando se seleccione
+ en el campo "IVA cobrado a nivel de fábrica" el Código 01
+ o en el campo de "Código del impuesto" el código 07.
+ */
+
+ $calculados[$numLinea]['baseImponible'] = $calculados[$numLinea]['subTotal'];
+
+ foreach (AUTO_CALCULAR as $field) {
+ if (!isset($calculados[$field])) {
+ if ($field !== 'totalDesgloseImpuesto') {
+ $calculados[$field] = 0;
+ continue;
+ }
+ $calculados[$field] = [];
+ }
+ }
+
+}
+
+/**
+ * Elimina caracteres no permitidos
+ * Solo letras, numeros, espacios y signos de puntuacion comunes en detalle
+ */
+function limpiarTexto($texto, $largoMaximo = FALSE)
+{
+ $limpio = preg_replace('/[^\\p{L}\\p{N}\\s.,;:\\-\\_\\(\\)\\[\\]\\{\\}\\!\\?\'"\\n\\r]/u', '', $texto);
+
+ if ($largoMaximo === FALSE) {
+ return $limpio;
+ }
+ // Trunca a la longitud máxima permitida
+ return mb_substr($limpio, 0, $largoMaximo);
+}
/* * ************************************************** */
/* Funcion para generar XML */
/* * ************************************************** */
function genXMLFe()
+{
+ return genXMLGenerico('FE');
+}
+
+function genXMLTe()
+{
+ return genXMLGenerico('TE');
+}
+
+function genXMLNc()
+{
+ return genXMLGenerico('NC');
+}
+
+function genXMLNd()
+{
+ return genXMLGenerico('ND');
+}
+
+function genXMLGenerico($tipoDocumento = 'FE')
{
// Datos contribuyente
$clave = params_get("clave");
$proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
+ $codigoActividadEmisor = params_get("codigo_actividad_emisor");
+ // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
$codigoActividadReceptor = params_get("codigo_actividad_receptor");
$consecutivo = params_get("consecutivo");
$fechaEmision = params_get("fecha_emision");
@@ -57,11 +403,11 @@ function genXMLFe()
$emisorOtrasSenas = params_get("emisor_otras_senas");
$emisorCodPaisTel = params_get("emisor_cod_pais_tel");
$emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email"); // This API only supports one email address for emisor
+ // This API only supports one email address for emisor
+ $emisorEmail = params_get("emisor_email");
$registroFiscal8707 = params_get("registrofiscal8707");
// Datos receptor
- $omitir_receptor = params_get("omitir_receptor"); // Deprecated
$receptorNombre = params_get("receptor_nombre");
$receptorTipoIdentif = params_get("receptor_tipo_identif");
$receptorNumIdentif = params_get("receptor_num_identif");
@@ -83,26 +429,10 @@ function genXMLFe()
$plazoCredito = params_get("plazo_credito");
$codMoneda = params_get("cod_moneda");
$tipoCambio = params_get("tipo_cambio");
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalServExonerados = params_get("total_serv_exonerados");
- $totalServNoSujeto = params_get("total_serv_no_sujeto");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalMercExonerada = params_get("total_merc_exonerada");
- $totalMercNoSujeta = params_get("total_merc_no_sujeta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalExonerado = params_get("total_exonerado");
- $totalNoSujeto = params_get("total_no_sujeto");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
+
$totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
$totalIVADevuelto = params_get("totalIVADevuelto");
$totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
$otros = json_decode(params_get('otros'));
@@ -161,18 +491,65 @@ function genXMLFe()
//Delimita el array a solo 4 elementos
$mediosPago = array_slice($mediosPago, 0, 4);
}
- }
-
- $xmlString = '
-
- ' . $clave . '
+ } else {
+ if (!in_array($condVenta, ['02', '08', '10'], true)) {
+ tools_reply([
+ "Status" => 400,
+ "text" => [
+ "medios_pago" => $mediosPago,
+ "mensaje" => "El campo 'medios_pago' es obligatorio cuando 'condicion_venta' no es '02', '08' o '10'"
+ ]
+ ], true);
+ }
+ }
+
+ $xmlClosingTag = '';
+
+ switch ($tipoDocumento) {
+ case 'FE':
+ $xmlString = '
+ ';
+ $xmlClosingTag = '';
+ break;
+ case 'TE':
+ $xmlString = '
+ ';
+ $xmlClosingTag = '';
+ break;
+ case 'NC':
+ $xmlString = '
+ ';
+ $xmlClosingTag = '';
+ break;
+ case 'ND':
+ $xmlString = '
+ ';
+ $xmlClosingTag = '';
+ break;
+ default:
+ tools_reply([
+ "Status" => 400,
+ "text" => "El campo 'tipoDocumento' debe ser uno de los siguientes valores: FE, TE, NC, ND"
+ ], true);
+ }
+
+ $xmlString .= '' . $clave . '
' . $proveedorSistemas . '
' . $codigoActividadEmisor . '';
- if (isset($codigoActividadReceptor) && $codigoActividadReceptor != "") {
+ if ($tipoDocumento == "FE" && isset($codigoActividadReceptor) && $codigoActividadReceptor != "") {
$codigoActividadReceptor = str_pad($codigoActividadReceptor, 6, "0", STR_PAD_LEFT);
if (strlen($codigoActividadReceptor) != CODIGOACTIVIDADSIZE) {
error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadReceptor is " . $codigoActividadReceptor);
@@ -202,18 +579,17 @@ function genXMLFe()
' . $emisorNombreComercial . '';
}
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
+ if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito) {
+ $xmlString .= '
' . $emisorProv . '
' . $emisorCanton . '
' . $emisorDistrito . '';
if ($emisorBarrio != '') {
$xmlString .= '' . $emisorBarrio . '';
}
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
+ $xmlString .= '123456';
+
+ $xmlString .= '';
}
if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
@@ -225,19 +601,21 @@ function genXMLFe()
}
if (preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
+ $xmlString .= '' . trim($emisorEmail) . '';
} else {
error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
}
- $xmlString .= '
+ $xmlString .= '
' . $receptorNombre . '';
- $xmlString .= '
-
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
+ if (!empty($receptorTipoIdentif) && !empty($receptorNumIdentif)) {
+ $xmlString .= '
+
+ ' . $receptorTipoIdentif . '
+ ' . $receptorNumIdentif . '
+ ';
+ }
if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
$xmlString .= '
@@ -297,6 +675,14 @@ function genXMLFe()
if (isset($plazoCredito) && $plazoCredito != "") {
$xmlString .= '
' . $plazoCredito . '';
+ } else {
+ // es credito segun la condicion de venta
+ if ($condVenta == "02") {
+ tools_reply([
+ "Status" => 400,
+ "text" => "El campo 'plazoCredito' es obligatorio cuando 'condicionVenta' es '02'"
+ ], true);
+ }
}
$xmlString .= '
@@ -432,25 +818,22 @@ function genXMLFe()
*/
$l = 1;
-
- foreach ($detalles as $d) {
+ $calculados = [];
- foreach (["codigoCABYS","subTotal","impuestoAsumidoEmisorFabrica","impuestoNeto"] as $requiredField) {
- if (!isset($d->{$requiredField}) || $d->{$requiredField} === '') {
- tools_reply("Se requiere el campo $requiredField en el detalle #$l", true);
- }
- }
+ foreach ($detalles as $d) {
$xmlString .= '
' . $l . '';
- $xmlString .= '
+ if (isset($d->codigoCABYS) && $d->codigoCABYS != "") {
+ $xmlString .= '
' . $d->codigoCABYS . '';
+ }
if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
// Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
+ $codigoComercialArray = (array) $d->codigoComercial;
// Delimitar el array a solo 5 elementos
if (count($codigoComercialArray) > 5) {
@@ -460,7 +843,7 @@ function genXMLFe()
// Iterar sobre los elementos del array
foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
+ $c = (array) $codigos;
// Verificar si el elemento es un array asociativo
if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
$xmlString .= '
@@ -483,8 +866,10 @@ function genXMLFe()
$xmlString .= '
' . $d->unidadMedidaComercial . '';
}
- $xmlString .= '
- ' . $d->detalle . '';
+
+ $d->detalle = limpiarTexto($d->detalle, 150);
+
+ $xmlString .= '' . $d->detalle . '';
if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
$xmlString .= '' . $d->numeroVINoSerie . '';
}
@@ -579,12 +964,12 @@ function genXMLFe()
$xmlString .= '';
}
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
+ $totalDescuentos = 0;
+ $xmlStringDescuento = '';
+ // Procesar descuentos, si existen
if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
+
+ $descuentoArray = (array) $d->descuento;
if (count($descuentoArray) > 5) {
error_log("descuento: " . count($descuentoArray) . " is greater than 5");
@@ -592,14 +977,14 @@ function genXMLFe()
$descuentoArray = array_slice($descuentoArray, 0, 5);
foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
+ $c = (array) $descuentos;
if (
is_array($c) &&
isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
) {
- $xmlString .= '
-
+ $c['montoDescuento'] = round(floatval($c['montoDescuento']), DECIMALES) * $d->cantidad;
+ $xmlStringDescuento = '
' . $c['montoDescuento'] . '
' . $c['codigoDescuento'] . '';
// CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
@@ -608,64 +993,87 @@ function genXMLFe()
isset($c['codigoDescuentoOTRO']) &&
strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
+ $xmlStringDescuento .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
}
// NaturalezaDescuento: minOccurs=0, longitud 3-80
if (
isset($c['naturalezaDescuento']) &&
strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
+ $xmlStringDescuento .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
}
- $xmlString .= '
- ';
+ $xmlStringDescuento .= '';
+ $totalDescuentos += $c['montoDescuento'];
}
}
}
- $xmlString .= '' . $d->subTotal . '';
+ // Calcular los valores que se pueden derivar de los datos ingresados
+ autoCalcularLinea($l, $d, $totalDescuentos, $calculados);
+
+ $xmlString .= '' . round(floatval($d->precioUnitario), DECIMALES) . '';
+ $xmlString .= '' . $calculados[$l]['montoTotal'] . '';
+ $xmlString .= $xmlStringDescuento;
+ $xmlString .= '' . $calculados[$l]['subTotal'] . '';
if (isset($d->IVACobradoFabrica) && $d->IVACobradoFabrica != "") {
$xmlString .= '' . $d->IVACobradoFabrica . '';
}
- if (isset($d->baseImponible) && $d->baseImponible != "") {
- $xmlString .= '' . $d->baseImponible . '';
- }
+ $xmlString .= '' . $calculados[$l]['baseImponible'] . '';
if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
+ foreach ($d->impuesto as $detalleImpuesto) {
$xmlString .= '
- ' . $i->codigo . '';
+ ' . $detalleImpuesto->codigo . '';
// Add if required
if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
+ isset($detalleImpuesto->codigo) && $detalleImpuesto->codigo == "99" &&
+ isset($detalleImpuesto->codigoImpuestoOtro) && !empty($detalleImpuesto->codigoImpuestoOtro)
) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
+ $xmlString .= '' . $detalleImpuesto->codigoImpuestoOtro . '';
}
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '' . $i->codigoTarifa . '';
+ // calcular el monto de impuesto si no se proporciona usando el codigo y la tarifa
+ if ($detalleImpuesto->codigo === "01" && isset($detalleImpuesto->codigoTarifa)) {
+
+ $impuestoPorTarifa = [
+ "01" => 0, // 0% (Artículo 32, num 1, RLIVA)
+ "02" => 0.01, // Tarifa reducida 1%
+ "03" => 0.02, // Tarifa reducida 2%
+ "04" => 0.04, // Tarifa reducida 4%
+ "05" => 0, // Transitorio 0%
+ "06" => 0.04, // Transitorio 4%
+ "07" => 0.08, // Tarifa transitoria 8%
+ "08" => 0.13, // Tarifa general 13%
+ "09" => 0.05, // Tarifa reducida 0.5%
+ "10" => 0, // Tarifa Exenta
+ "11" => 0 // Tarifa 0% sin derecho a crédito
+ ];
+ $calculados[$l]['tarifa'] = $impuestoPorTarifa[$detalleImpuesto->codigoTarifa] * 100;
+
+ $calculados[$l]['monto'] = round(floatval($calculados[$l]['baseImponible']) * $impuestoPorTarifa[$detalleImpuesto->codigoTarifa], DECIMALES);
}
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '' . $i->tarifa . '';
+ if (isset($detalleImpuesto->codigoTarifa) && $detalleImpuesto->codigoTarifa != "") {
+ $xmlString .= '' . $detalleImpuesto->codigoTarifa . '';
}
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '' . $i->factorIVA . '';
+ $xmlString .= '' . $calculados[$l]['tarifa'] . '';
+
+ if (isset($detalleImpuesto->factorIVA) && $detalleImpuesto->factorIVA != "") {
+ $xmlString .= '' . $detalleImpuesto->factorIVA . '';
}
if (
- isset($i->codigo) &&
- in_array($i->codigo, ["03", "04", "05", "06"]) &&
- isset($i->datosImpuestoEspecifico) &&
- is_object($i->datosImpuestoEspecifico)
+ isset($detalleImpuesto->codigo) &&
+ in_array($detalleImpuesto->codigo, ["03", "04", "05", "06"]) &&
+ isset($detalleImpuesto->datosImpuestoEspecifico) &&
+ is_object($detalleImpuesto->datosImpuestoEspecifico)
) {
- $datosImpuestoEsp = $i->datosImpuestoEspecifico;
+ $datosImpuestoEsp = $detalleImpuesto->datosImpuestoEspecifico;
$xmlString .= '';
if (isset($datosImpuestoEsp->cantidadUnidadMedida)) {
$xmlString .= '' . $datosImpuestoEsp->cantidadUnidadMedida . '';
@@ -685,45 +1093,154 @@ function genXMLFe()
$xmlString .= '';
}
- $xmlString .= '' . $i->monto . '';
+ if (!isset($detalleImpuesto->monto) || ($detalleImpuesto->monto === 0 && $calculados[$l]['monto'] != 0)) {
+ $detalleImpuesto->{"monto"} = $calculados[$l]['monto'];
+ }
+
+ if ($detalleImpuesto->monto === 0 && $detalleImpuesto->monto != $calculados[$l]['monto']) {
+ tools_reply("El monto del impuesto no coincide con el calculado para la línea $l, Monto: " . $detalleImpuesto->monto . ", Calculado: " . $calculados[$l]['monto'], true);
+ }
+
+ // Procesar exoneración, si existe
+ $xmlStringExoneracion = '';
+ if (isset($detalleImpuesto->exoneracion) && $detalleImpuesto->exoneracion != "") {
- if (isset($i->exoneracion) && $i->exoneracion != "") {
- $xmlString .= '
+ // Validar exoneración con la API de Hacienda (opcional)
+ $datosExoneracion = consultarYMapearExoneracion($detalleImpuesto->exoneracion->numeroDocumento);
+ if ($datosExoneracion === false) {
+ tools_reply([
+ "Status" => 400,
+ "text" => "Advertencia: No se pudo validar la exoneración en Hacienda " . $detalleImpuesto->exoneracion->numeroDocumento,
+ ], true);
+ }
+ $detalleImpuesto->exoneracion = (object) array_merge((array) $detalleImpuesto->exoneracion, (array) $datosExoneracion);
+
+ // exonera lo mas que permita ser exonerado por el impuesto aplicado
+ $porcentajeExonerado = ($detalleImpuesto->exoneracion->tarifaExoneracion > $impuestoPorTarifa[$detalleImpuesto->codigoTarifa] * 100) ? $impuestoPorTarifa[$detalleImpuesto->codigoTarifa] * 100 : $detalleImpuesto->exoneracion->tarifaExoneracion;
+ $detalleImpuesto->exoneracion->montoExoneracion = $calculados[$l]['subTotal'] * ($porcentajeExonerado / 100);
+
+ $xmlStringExoneracion .= '
- ' . $i->exoneracion->tipoDocumento . '';
- if (isset($i->exoneracion->tipoDocumentoOtro) && !empty($i->exoneracion->tipoDocumentoOtro)) {
- $xmlString .= '' . $i->exoneracion->tipoDocumentoOtro . '';
+ ' . $detalleImpuesto->exoneracion->tipoDocumento . '';
+ if (isset($detalleImpuesto->exoneracion->tipoDocumentoOtro) && !empty($detalleImpuesto->exoneracion->tipoDocumentoOtro)) {
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->tipoDocumentoOtro . '';
}
- $xmlString .= '' . $i->exoneracion->numeroDocumento . '';
- if (isset($i->exoneracion->numeroArticulo) && !empty($i->exoneracion->numeroArticulo)) {
- $xmlString .= '' . $i->exoneracion->numeroArticulo . '';
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->numeroDocumento . '';
+ if (isset($detalleImpuesto->exoneracion->numeroArticulo) && !empty($detalleImpuesto->exoneracion->numeroArticulo)) {
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->numeroArticulo . '';
}
- if (isset($i->exoneracion->numeroInciso) && !empty($i->exoneracion->numeroInciso)) {
- $xmlString .= '' . $i->exoneracion->numeroInciso . '';
+ if (isset($detalleImpuesto->exoneracion->numeroInciso) && !empty($detalleImpuesto->exoneracion->numeroInciso)) {
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->numeroInciso . '';
}
- $xmlString .= '' . $i->exoneracion->nombreInstitucion . '';
- if (isset($i->exoneracion->nombreInstitucionOtros) && !empty($i->exoneracion->nombreInstitucionOtros)) {
- $xmlString .= '' . $i->exoneracion->nombreInstitucionOtros . '';
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->nombreInstitucion . '';
+ if (isset($detalleImpuesto->exoneracion->nombreInstitucionOtros) && !empty($detalleImpuesto->exoneracion->nombreInstitucionOtros)) {
+ $xmlStringExoneracion .= '' . $detalleImpuesto->exoneracion->nombreInstitucionOtros . '';
}
- $xmlString .= '
- ' . $i->exoneracion->fechaEmision . '
- ' . $i->exoneracion->tarifaExoneracion . '
- ' . $i->exoneracion->montoExoneracion . '
+ $xmlStringExoneracion .= '
+ ' . $detalleImpuesto->exoneracion->fechaEmision . '
+ ' . $detalleImpuesto->exoneracion->tarifaExoneracion . '
+ ' . $detalleImpuesto->exoneracion->montoExoneracion . '
';
+
}
+ $xmlString .= '' . $detalleImpuesto->monto . '';
+ $xmlString .= $xmlStringExoneracion;
$xmlString .= '';
+
+ if (isset($detalleImpuesto->codigo) && isset($detalleImpuesto->monto)) {
+
+ foreach (['impuestoNeto'] as $key) {
+ if (!isset($calculados[$l][$key])) {
+ $calculados[$l][$key] = 0;
+ }
+ }
+
+ foreach (['totalMercExonerada', 'totalServExonerado'] as $key) {
+ if (!isset($calculados[$key])) {
+ $calculados[$key] = 0;
+ }
+ }
+
+ if (isset($detalleImpuesto->exoneracion) && isset($detalleImpuesto->exoneracion->montoExoneracion)) {
+ $calculados[$l]['impuestoNeto'] += round($detalleImpuesto->monto - $detalleImpuesto->exoneracion->montoExoneracion, DECIMALES);
+ } else {
+ $calculados[$l]['impuestoNeto'] += round($detalleImpuesto->monto, DECIMALES);
+ }
+
+ // 4.4 se basa en los cabys para saber que es un servicio o mercaderia,
+ // los codigos que empiezan de 0 a 4 son mercaderias y los que empiezan de 5 a 9 son servicios
+ $esServicio = (isset($d->codigoCABYS) && !empty($d->codigoCABYS) && in_array($d->codigoCABYS[0], ['5', '6', '7', '8', '9'], true));
+ $esExento = ($detalleImpuesto->codigoTarifa === "10" || $detalleImpuesto->codigoTarifa === "11");
+
+ if ($esExento) {
+ // Exento
+ $calculados[$esServicio ? "totalServExentos" : "totalMercanciasExentas"] += $calculados[$l]['montoTotal'];
+ $calculados['totalExento'] += $calculados[$l]['montoTotal'];
+ $calculados['totalImpuesto'] += $detalleImpuesto->monto;
+ } else {
+
+ // llave para el mapa de desglose de impuestos por código y tarifa
+ $keyDesgloseImpuesto = $detalleImpuesto->codigo . "-" . $detalleImpuesto->codigoTarifa;
+ if (!isset($calculados['totalDesgloseImpuesto'][$keyDesgloseImpuesto])) {
+ $montoDesgloseImpuesto = $detalleImpuesto->monto;
+ } else {
+ $montoDesgloseImpuesto = $calculados['totalDesgloseImpuesto'][$keyDesgloseImpuesto]->TotalMontoImpuesto + $detalleImpuesto->monto;
+ }
+
+ if (isset($detalleImpuesto->exoneracion) && isset($detalleImpuesto->exoneracion->montoExoneracion)) {
+ // hay monto exonerado
+ $montoExonerado = $porcentajeExonerado / $detalleImpuesto->exoneracion->tarifaExoneracion * $calculados[$l]['montoTotal'];
+ $calculados[$l]['montoTotal'] -= $montoExonerado;
+ $calculados['totalImpuesto'] += $detalleImpuesto->monto - $detalleImpuesto->exoneracion->montoExoneracion;
+
+ $calculados['totalDesgloseImpuesto'][$keyDesgloseImpuesto] = (object) [
+ "Codigo" => $detalleImpuesto->codigo,
+ "CodigoTarifaIVA" => $detalleImpuesto->codigoTarifa,
+ "TotalMontoImpuesto" => ($montoDesgloseImpuesto - $detalleImpuesto->exoneracion->montoExoneracion)
+ ];
+ } else {
+ // no se exoneró nada
+ $calculados['totalImpuesto'] += $detalleImpuesto->monto;
+ $montoExonerado = 0;
+
+ $calculados['totalDesgloseImpuesto'][$keyDesgloseImpuesto] = (object) [
+ "Codigo" => $detalleImpuesto->codigo,
+ "CodigoTarifaIVA" => $detalleImpuesto->codigoTarifa,
+ "TotalMontoImpuesto" => $montoDesgloseImpuesto
+ ];
+ }
+
+ $calculados[$esServicio ? "totalServExonerado" : "totalMercExonerada"] += $montoExonerado;
+ $calculados['totalExonerado'] += $montoExonerado;
+ $calculados[$esServicio ? "totalServGravados" : "totalMercanciasGravadas"] += $calculados[$l]['montoTotal'];
+ $calculados['totalGravado'] += $calculados[$l]['montoTotal'];
+ }
+ }
}
}
- $xmlString .= '' . $d->impuestoAsumidoEmisorFabrica . '';
- $xmlString .= '' . $d->impuestoNeto . '';
- $xmlString .= '' . $d->montoTotalLinea . '';
+ if (isset($d->impuestoAsumidoEmisorFabrica) && $d->impuestoAsumidoEmisorFabrica != "") {
+ $xmlString .= '' . $d->impuestoAsumidoEmisorFabrica . '';
+ } else {
+ $xmlString .= '0';
+ }
+
+ $xmlString .= '' . $calculados[$l]['impuestoNeto'] . '';
+
+ // Se calcula de la siguiente manera: se obtiene de la sumatoria de los campos “Subtotal”, “Impuesto Neto”.
+ $calculados[$l]['montoTotalLinea'] = $calculados[$l]['subTotal'] + $calculados[$l]['impuestoNeto'];
+
+ $xmlString .= '' . $calculados[$l]['montoTotalLinea'] . '';
$xmlString .= '';
+
+ validarLinea($l, $d);
+
$l++;
}
$xmlString .= '';
+
//OtrosCargos
if (isset($otrosCargos) && $otrosCargos != "") {
foreach ($otrosCargos as $o) {
@@ -745,6 +1262,7 @@ function genXMLFe()
$xmlString .= '
' . $o->nombreTercero . '';
}
+ $o->detalle = limpiarTexto($o->detalle, 150);
$xmlString .= '
' . $o->detalle . '';
if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
@@ -758,115 +1276,72 @@ function genXMLFe()
}
}
+ foreach ($calculados as $key => $value) {
+ if (is_numeric($value)) {
+ $calculados[$key] = round($value, DECIMALES);
+ }
+ }
+
$xmlString .= '
';
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
+ $xmlString .= '
' . $codMoneda . '
' . $tipoCambio . '
';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalServExonerados != '') {
- $xmlString .= '
- ' . $totalServExonerados . '';
- }
-
- if ($totalServNoSujeto != '') {
- $xmlString .= '
- ' . $totalServNoSujeto . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalMercExonerada != '') {
- $xmlString .= '
- ' . $totalMercExonerada . '';
- }
-
- if ($totalMercNoSujeta != '') {
- $xmlString .= '
- ' . $totalMercNoSujeta . '';
- }
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
+ // Se obtiene de la suma de los campos "Total servicios No Sujetos de IVA" mas "Total mercancías No Sujetas de IVA".
+ // @TODO revisar no sujetos
+ // $calculados['totalNoSujeto'] = $calculados['totalServNoSujetos'] + $calculados['totalMercanciasNoSujetas'];
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
+ // Calcular totalVenta
+ // Se obtiene de la sumatoria de los campos “total gravado”, “total exento”, “Total Exonerado” y “Total No Sujeto
+ $calculados['totalVenta'] =
+ $calculados['totalGravado'] +
+ $calculados['totalExento'] +
+ $calculados['totalExonerado'] +
+ $calculados['totalNoSujeto'];
- if ($totalExonerado != '') {
- $xmlString .= '
- ' . $totalExonerado . '';
- }
+ // Calcular totalVentaNeta
+ // Se obtiene de la resta de los campos total venta menos total descuento
+ $calculados['totalVentaNeta'] = $calculados['totalVenta'] - $calculados['totalDescuentos'];
- if ($totalNoSujeto != '') {
- $xmlString .= '
- ' . $totalNoSujeto . '';
+ // totalComprobante: Se obtiene de la suma de los campos "total venta neta", "monto total del impuesto" y "total otros cargos" menos "total IVA devuelto", en caso de contar con dichos campos.
+ $calculados['totalComprobante'] = $calculados['totalVentaNeta'] + $calculados['totalImpuesto'];
+ if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
+ $calculados['totalComprobante'] += $totalOtrosCargos;
}
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
+ if ($totalIVADevuelto != '') {
+ $calculados['totalComprobante'] -= $totalIVADevuelto;
}
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
+ // agrega los campos del resumen que esten en el array calculados y no esten vacios
+ foreach (AUTO_CALCULAR as $campoResumen) {
+ if ($campoResumen == "totalDesgloseImpuesto") {
+ // Add logic for TotalDesgloseImpuesto
+ $totalDesgloseImpuesto = $calculados['totalDesgloseImpuesto'] ?? [];
+ if (count($totalDesgloseImpuesto) > 0) {
+ foreach ($totalDesgloseImpuesto as $impuesto) {
+ $xmlString .= '
+ ';
+ if (isset($impuesto->Codigo)) {
+ $xmlString .= '' . $impuesto->Codigo . '';
+ }
+ if (isset($impuesto->CodigoTarifaIVA)) {
+ $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
+ }
+ if (isset($impuesto->TotalMontoImpuesto)) {
+ $xmlString .= '' . round($impuesto->TotalMontoImpuesto, DECIMALES) . '';
+ }
+ $xmlString .= '';
+ }
}
- $xmlString .= '';
+ continue;
}
- }
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
+ if ($calculados[$campoResumen] != '') {
+ $xmlString .= '<' . ucfirst($campoResumen) . '>' . $calculados[$campoResumen] . '' . ucfirst($campoResumen) . '>';
+ }
}
if ($totalImpAsumidoEmisorFabrica != '') {
@@ -885,7 +1360,14 @@ function genXMLFe()
}
if (isset($mediosPago) && !empty($mediosPago)) {
+ $totalMediosPago = 0;
foreach ($mediosPago as $o) {
+
+ // usar el total de comprobante como pago cuando se pasa un -1
+ if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago) && $o->totalMedioPago == -1) {
+ $o->totalMedioPago = $calculados['totalComprobante'];
+ }
+
$xmlString .= '
';
@@ -900,18 +1382,26 @@ function genXMLFe()
}
// Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
+ if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago) && $o->totalMedioPago >= 0) {
+ $xmlString .= '' . round($o->totalMedioPago, DECIMALES) . '';
}
$xmlString .= '';
+ $totalMediosPago += floatval($o->totalMedioPago);
+ }
+
+ $t1 = round((float) $totalMediosPago, DECIMALES);
+ $t2 = round((float) $calculados['totalComprobante'], DECIMALES);
+ if (abs($t1 - $t2) > 1) {
+ tools_reply("El total de los medios de pago no coincide con el total del comprobante. Total Medios de Pago: [$t1], Total Comprobante: [$t2]", true);
}
}
- $xmlString .= '
- ' . $totalComprobante . '
- ';
+ $xmlString .= '' . $calculados['totalComprobante'] . '';
+ $xmlString .= ' ';
+
+ // Información Referencia
if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
foreach ($informacionReferencia as $ref) {
if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
@@ -942,36 +1432,6 @@ function genXMLFe()
}
}
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
// Start Otros element
$xmlString .= '';
@@ -1006,7 +1466,6 @@ function genXMLFe()
$xmlString .= '';
-
// XML Resultante
//
// Texto opcional 1
@@ -1026,4318 +1485,38 @@ function genXMLFe()
//
//
- $xmlString .= '
- ';
- $arrayResp = array(
+ // Cierre del XML
+ $xmlString .= $xmlClosingTag;
+
+ // eliminar espacios en blanco para reducir el tamaño del XML
+ $xmlString = preg_replace('/>\s+', '><', $xmlString);
+
+ $validacion = ValidadorXML::validateXml($xmlString, $consecutivo);
+
+ if ($validacion && $validacion->status == "error") {
+ unset($validacion->status);
+ $validacion->schema = basename($validacion->schema);
+ if (conf_get('mode', 'core', 'web') == 'cli') {
+ return tools_reply([
+ "Status" => 400,
+ "text" => array(
+ "clave" => $clave,
+ "validacion" => $validacion
+ )
+ ], true);
+ }
+
+ tools_reply([
+ "Status" => 400,
+ "text" => array(
+ "clave" => $clave,
+ "validacion" => $validacion
+ )
+ ], true);
+ }
+
+ return array(
"clave" => $clave,
"xml" => base64_encode($xmlString)
);
-
- return $arrayResp;
}
-
-function genXMLNC()
-{
-
- // Datos contribuyente
- $clave = params_get("clave");
- $proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $codigoActividadReceptor = params_get("codigo_actividad_receptor");
- $consecutivo = params_get("consecutivo");
- $fechaEmision = params_get("fecha_emision");
-
- // Datos emisor
- $emisorNombre = params_get("emisor_nombre");
- $emisorTipoIdentif = params_get("emisor_tipo_identif");
- $emisorNumIdentif = params_get("emisor_num_identif");
- $emisorNombreComercial = params_get("emisor_nombre_comercial");
- $emisorProv = params_get("emisor_provincia");
- $emisorCanton = params_get("emisor_canton");
- $emisorDistrito = params_get("emisor_distrito");
- $emisorBarrio = params_get("emisor_barrio");
- $emisorOtrasSenas = params_get("emisor_otras_senas");
- $emisorCodPaisTel = params_get("emisor_cod_pais_tel");
- $emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email");
- $registroFiscal8707 = params_get("registrofiscal8707");
-
- // Datos receptor
- $omitir_receptor = params_get("omitir_receptor"); // Deprecated
- $receptorNombre = params_get("receptor_nombre");
- $receptorTipoIdentif = params_get("receptor_tipo_identif");
- $receptorNumIdentif = params_get("receptor_num_identif");
- $receptorIdentifExtranjero = params_get("receptor_identif_extranjero");
- $receptorNombreComercial = params_get("receptor_nombre_comercial");
- $receptorProvincia = params_get("receptor_provincia");
- $receptorCanton = params_get("receptor_canton");
- $receptorDistrito = params_get("receptor_distrito");
- $receptorBarrio = params_get("receptor_barrio");
- $receptorOtrasSenas = params_get("receptor_otras_senas");
- $receptorOtrasSenasExtranjero = params_get("receptor_otras_senas_extranjero");
- $receptorCodPaisTel = params_get("receptor_cod_pais_tel");
- $receptorTel = params_get("receptor_tel");
- $receptorEmail = params_get("receptor_email");
-
- // Detalles de tiquete / Factura
- $condVenta = params_get("condicion_venta");
- $condVentaOtros = params_get("condicion_venta_otros");
- $plazoCredito = params_get("plazo_credito");
- $codMoneda = params_get("cod_moneda");
- $tipoCambio = params_get("tipo_cambio");
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalServExonerados = params_get("total_serv_exonerados");
- $totalServNoSujeto = params_get("total_serv_no_sujeto");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalMercExonerada = params_get("total_merc_exonerada");
- $totalMercNoSujeta = params_get("total_merc_no_sujeta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalExonerado = params_get("total_exonerado");
- $totalNoSujeto = params_get("total_no_sujeto");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
- $totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
- $totalIVADevuelto = params_get("totalIVADevuelto");
- $totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
-
- $otros = json_decode(params_get('otros'));
-
- // Detalles de la compra
- $detalles = json_decode(params_get("detalles"));
- $informacionReferencia = json_decode(params_get("informacion_referencia"));
- $otrosCargos = json_decode(params_get("otrosCargos"));
- $mediosPago = json_decode(params_get("medios_pago"));
- // Resumen
- $totalDesgloseImpuesto = json_decode(params_get("totalDesgloseImpuesto"));
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- grace_debug(params_get("otrosCargos"));
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- grace_debug(params_get("medios_pago"));
- }
-
- if (isset($totalDesgloseImpuesto) && $totalDesgloseImpuesto != "") {
- grace_debug(params_get("totalDesgloseImpuesto"));
- }
-
- // Validate string sizes
- $codigoActividadEmisor = str_pad($codigoActividadEmisor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadEmisor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadEmisor is " . $codigoActividadEmisor);
- }
-
- if (strlen($emisorNombre) > EMISORNOMBREMAXSIZE) {
- error_log("emisorNombreSize: " . EMISORNOMBREMAXSIZE . " is greater than emisorNombre: " . $emisorNombre);
- }
-
- if (strlen($receptorNombre) > RECEPTORNOMBREMAXSIZE) {
- error_log("receptorNombreMaxSize: " . RECEPTORNOMBREMAXSIZE . " is greater than receptorNombre: " . $receptorNombre);
- }
-
- if (strlen($receptorOtrasSenas) > RECEPTOROTRASSENASMAXSIZE) {
- error_log("RECEPTOROTRASSENASEXTRANJEROMAXSIZE: " . RECEPTOROTRASSENASMAXSIZE . " is greater than receptorOtrasSenas: " . $receptorOtrasSenas);
- }
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- if (count($otrosCargos) > 15) {
- error_log("otrosCargos: " . count($otrosCargos) . " is greater than 15");
- //Delimita el array a solo 15 elementos
- $otrosCargos = array_slice($otrosCargos, 0, 15);
- }
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- if (count($mediosPago) > 4) {
- error_log("otrosCargos: " . count($mediosPago) . " is greater than 4");
- //Delimita el array a solo 4 elementos
- $mediosPago = array_slice($mediosPago, 0, 4);
- }
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $proveedorSistemas . '
- ' . $codigoActividadEmisor . '';
-
- if (isset($codigoActividadReceptor) && $codigoActividadReceptor != "") {
- $codigoActividadReceptor = str_pad($codigoActividadReceptor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadReceptor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadReceptor is " . $codigoActividadReceptor);
- }
-
- $xmlString .= '
- ' . $codigoActividadReceptor . '';
- }
-
- $xmlString .= '
- ' . $consecutivo . '
- ' . $fechaEmision . '
-
- ' . $emisorNombre . '
-
- ' . $emisorTipoIdentif . '
- ' . $emisorNumIdentif . '
- ';
-
- if (isset($registroFiscal8707) && $registroFiscal8707 != "") {
- $xmlString .= '
- ' . $registroFiscal8707 . '';
- }
-
- if (isset($emisorNombreComercial) && $emisorNombreComercial != "") {
- $xmlString .= '
- ' . $emisorNombreComercial . '';
- }
-
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $emisorProv . '
- ' . $emisorCanton . '
- ' . $emisorDistrito . '';
- if ($emisorBarrio != '') {
- $xmlString .= '' . $emisorBarrio . '';
- }
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
- }
-
- if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
- $xmlString .= '
-
- ' . $emisorCodPaisTel . '
- ' . $emisorTel . '
- ';
- }
-
- if (preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
- } else {
- error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
- }
-
- if ($omitir_receptor != 'true') {
- $xmlString .= '
- ' . $receptorNombre . '';
-
- if ($receptorTipoIdentif != '' && $receptorNumIdentif != '') {
- $xmlString .= '
-
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
- }
-
- if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
- $xmlString .= '
- '
- . $receptorIdentifExtranjero .
- '';
- }
-
- if (isset($receptorNombreComercial) && $receptorNombreComercial != "") {
- $xmlString .= '
- ' . $receptorNombreComercial . '';
- }
-
- if ($receptorProvincia != '' && $receptorCanton != '' && $receptorDistrito != '' && $receptorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $receptorProvincia . '
- ' . $receptorCanton . '
- ' . $receptorDistrito . '';
- if ($receptorBarrio != '') {
- $xmlString .= '
- ' . $receptorBarrio . '';
- }
- $xmlString .= '
- ' . $receptorOtrasSenas . '
- ';
- }
-
- if ($receptorOtrasSenasExtranjero != '' && strlen($receptorOtrasSenasExtranjero) <= RECEPTOROTRASSENASEXTRANJEROMAXSIZE) {
- $xmlString .= '
- '
- . $receptorOtrasSenasExtranjero .
- '';
- }
-
- if ($receptorCodPaisTel != '' && $receptorTel != '') {
- $xmlString .= '
-
- ' . $receptorCodPaisTel . '
- ' . $receptorTel . '
- ';
- }
-
- if ($receptorEmail != '') {
- $xmlString .= '' . $receptorEmail . '';
- }
-
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $condVenta . '';
-
- if (isset($condVentaOtros) && $condVentaOtros != "") {
- $xmlString .= '
- ' . $condVentaOtros . '';
- }
-
- if (isset($plazoCredito) && $plazoCredito != "") {
- $xmlString .= '
- ' . $plazoCredito . '';
- }
-
- $xmlString .= '
- ';
-
- /* EJEMPLO DE DETALLES
- {
- "1":["1","Sp","Honorarios","100000","100000","100000","100000","1000","Pronto pago",{"Imp": [{"cod": 122,"tarifa": 1,"monto": 100},{"cod": 133,"tarifa": 1,"monto": 1300}]}],
- "2":["1","Sp","Honorarios","100000","100000","100000","100000"]
- }
- */
- $l = 1;
- foreach ($detalles as $d) {
- $xmlString .= '
- ' . $l . '';
-
- if (isset($d->partidaArancelaria) && $d->partidaArancelaria != "") {
- $xmlString .= '' . $d->partidaArancelaria . '';
- }
-
- if (isset($d->codigoCABYS) && $d->codigoCABYS != "") {
- $xmlString .= '
- ' . $d->codigoCABYS . '';
- }
-
- if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
- // Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
-
- // Delimitar el array a solo 5 elementos
- if (count($codigoComercialArray) > 5) {
- error_log("codigoComercial: " . count($codigoComercialArray) . " is greater than 5");
- }
- $codigoComercialArray = array_slice($codigoComercialArray, 0, 5);
-
- // Iterar sobre los elementos del array
- foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
- // Verificar si el elemento es un array asociativo
- if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
- $xmlString .= '
-
- ' . $c['tipo'] . '
- ' . $c['codigo'] . '
- ';
- }
- }
- }
-
- $xmlString .= '
- ' . $d->cantidad . '
- ' . $d->unidadMedida . '';
- if (isset($d->tipoTransaccion) && $d->tipoTransaccion != "") {
- $xmlString .= '
- ' . $d->tipoTransaccion . '';
- }
- if (isset($d->unidadMedidaComercial) && $d->unidadMedidaComercial != "") {
- $xmlString .= '
- ' . $d->unidadMedidaComercial . '';
- }
- $xmlString .= '
- ' . $d->detalle . '';
- if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
- $xmlString .= '' . $d->numeroVINoSerie . '';
- }
-
- if (isset($d->registroMedicamento) && $d->registroMedicamento !== "") {
- $xmlString .= '' . htmlspecialchars($d->registroMedicamento) . '';
- }
- if (isset($d->formaFarmaceutica) && $d->formaFarmaceutica !== "") {
- $xmlString .= '' . htmlspecialchars($d->formaFarmaceutica) . '';
- }
-
- if (isset($d->detalleSurtido) && is_array($d->detalleSurtido) && count($d->detalleSurtido) > 0) {
- $xmlString .= '';
- $lineas = array_slice($d->detalleSurtido, 0, 20);
- foreach ($lineas as $linea) {
- $xmlString .= '';
- $xmlString .= '' . $linea->codigoCABYSSurtido . '';
- if (isset($linea->codigoComercialSurtido) && is_array($linea->codigoComercialSurtido)) {
- $codigos = array_slice($linea->codigoComercialSurtido, 0, 5);
- foreach ($codigos as $codigo) {
- $xmlString .= '';
- $xmlString .= '' . $codigo->tipoSurtido . '';
- $xmlString .= '' . $codigo->codigoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->cantidadSurtido . '';
- $xmlString .= '' . $linea->unidadMedidaSurtido . '';
- if (isset($linea->unidadMedidaComercialSurtido)) {
- $xmlString .= '' . $linea->unidadMedidaComercialSurtido . '';
- }
- $xmlString .= '' . $linea->detalleSurtido . '';
- $xmlString .= '' . $linea->precioUnitarioSurtido . '';
- $xmlString .= '' . $linea->montoTotalSurtido . '';
- if (isset($linea->descuentoSurtido) && is_array($linea->descuentoSurtido)) {
- $descuentos = array_slice($linea->descuentoSurtido, 0, 5);
- foreach ($descuentos as $desc) {
- $xmlString .= '';
- $xmlString .= '' . $desc->montoDescuentoSurtido . '';
- $xmlString .= '' . $desc->codigoDescuentoSurtido . '';
- if (isset($desc->descuentoSurtidoOtros)) {
- $xmlString .= '' . $desc->descuentoSurtidoOtros . '';
- }
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->subTotalSurtido . '';
- if (isset($linea->ivaCobradoFabricaSurtido)) {
- $xmlString .= '' . $linea->ivaCobradoFabricaSurtido . '';
- }
- $xmlString .= '' . $linea->baseImponibleSurtido . '';
- if (isset($linea->impuestoSurtido) && is_array($linea->impuestoSurtido)) {
- $impuestos = array_slice($linea->impuestoSurtido, 0, 1000);
- foreach ($impuestos as $imp) {
- $xmlString .= '';
- $xmlString .= '' . $imp->codigoImpuestoSurtido . '';
- if (isset($imp->codigoImpuestoOTROSurtido)) {
- $xmlString .= '' . $imp->codigoImpuestoOTROSurtido . '';
- }
- if (isset($imp->codigoTarifaIVASurtido)) {
- $xmlString .= '' . $imp->codigoTarifaIVASurtido . '';
- }
- if (isset($imp->tarifaSurtido)) {
- $xmlString .= '' . $imp->tarifaSurtido . '';
- }
- if (isset($imp->datosImpuestoEspecificoSurtido)) {
- $e = $imp->datosImpuestoEspecificoSurtido;
- $xmlString .= '';
- if (isset($e->cantidadUnidadMedidaSurtido)) {
- $xmlString .= '' . $e->cantidadUnidadMedidaSurtido . '';
- }
- if (isset($e->porcentajeSurtido)) {
- $xmlString .= '' . $e->porcentajeSurtido . '';
- }
- if (isset($e->proporcionSurtido)) {
- $xmlString .= '' . $e->proporcionSurtido . '';
- }
- if (isset($e->volumenUnidadConsumoSurtido)) {
- $xmlString .= '' . $e->volumenUnidadConsumoSurtido . '';
- }
- if (isset($e->impuestoUnidadSurtido)) {
- $xmlString .= '' . $e->impuestoUnidadSurtido . '';
- }
- $xmlString .= '';
- }
- $xmlString .= '' . $imp->montoImpuestoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
- if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
-
- if (count($descuentoArray) > 5) {
- error_log("descuento: " . count($descuentoArray) . " is greater than 5");
- }
- $descuentoArray = array_slice($descuentoArray, 0, 5);
-
- foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
- if (
- is_array($c) &&
- isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
- isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
- ) {
- $xmlString .= '
-
- ' . $c['montoDescuento'] . '
- ' . $c['codigoDescuento'] . '';
- // CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
- if (
- isset($c['codigoDescuento']) && $c['codigoDescuento'] === "99" &&
- isset($c['codigoDescuentoOTRO']) &&
- strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
- ) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
- }
- // NaturalezaDescuento: minOccurs=0, longitud 3-80
- if (
- isset($c['naturalezaDescuento']) &&
- strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
- ) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
- }
- $xmlString .= '
- ';
- }
- }
- }
-
- $xmlString .= '' . $d->subTotal . '';
-
- if (isset($d->IVACobradoFabrica) && $d->IVACobradoFabrica != "") {
- $xmlString .= '' . $d->IVACobradoFabrica . '';
- }
-
- if (isset($d->baseImponible) && $d->baseImponible != "") {
- $xmlString .= '' . $d->baseImponible . '';
- }
- if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
- $xmlString .= '
- ' . $i->codigo . '';
-
- // Add if required
- if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
- ) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
- }
-
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '' . $i->codigoTarifa . '';
- }
-
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '' . $i->tarifa . '';
- }
-
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '' . $i->factorIVA . '';
- }
-
- if (
- isset($i->codigo) &&
- in_array($i->codigo, ["03", "04", "05", "06"]) &&
- isset($i->datosImpuestoEspecifico) &&
- is_object($i->datosImpuestoEspecifico)
- ) {
- $datosImpuestoEsp = $i->datosImpuestoEspecifico;
- $xmlString .= '';
- if (isset($datosImpuestoEsp->cantidadUnidadMedida)) {
- $xmlString .= '' . $datosImpuestoEsp->cantidadUnidadMedida . '';
- }
- if (isset($datosImpuestoEsp->porcentaje)) {
- $xmlString .= '' . $datosImpuestoEsp->porcentaje . '';
- }
- if (isset($datosImpuestoEsp->proporcion)) {
- $xmlString .= '' . $datosImpuestoEsp->proporcion . '';
- }
- if (isset($datosImpuestoEsp->volumenUnidadConsumo)) {
- $xmlString .= '' . $datosImpuestoEsp->volumenUnidadConsumo . '';
- }
- if (isset($datosImpuestoEsp->impuestoUnidad)) {
- $xmlString .= '' . $datosImpuestoEsp->impuestoUnidad . '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '' . $i->monto . '';
-
- if (isset($i->montoExportacion) && $i->montoExportacion != "") {
- $xmlString .= '' . $i->montoExportacion . '';
- }
-
- if (isset($i->exoneracion) && $i->exoneracion != "") {
- $xmlString .= '
-
- ' . $i->exoneracion->tipoDocumento . '';
- if (isset($i->exoneracion->tipoDocumentoOtro) && !empty($i->exoneracion->tipoDocumentoOtro)) {
- $xmlString .= '' . $i->exoneracion->tipoDocumentoOtro . '';
- }
- $xmlString .= '' . $i->exoneracion->numeroDocumento . '';
- if (isset($i->exoneracion->numeroArticulo) && !empty($i->exoneracion->numeroArticulo)) {
- $xmlString .= '' . $i->exoneracion->numeroArticulo . '';
- }
- if (isset($i->exoneracion->numeroInciso) && !empty($i->exoneracion->numeroInciso)) {
- $xmlString .= '' . $i->exoneracion->numeroInciso . '';
- }
- $xmlString .= '' . $i->exoneracion->nombreInstitucion . '';
- if (isset($i->exoneracion->nombreInstitucionOtros) && !empty($i->exoneracion->nombreInstitucionOtros)) {
- $xmlString .= '' . $i->exoneracion->nombreInstitucionOtros . '';
- }
- $xmlString .= '
- ' . $i->exoneracion->fechaEmision . '
- ' . $i->exoneracion->tarifaExoneracion . '
- ' . $i->exoneracion->montoExoneracion . '
- ';
- }
-
- $xmlString .= '';
- }
- }
- if (isset($d->impuestoAsumidoEmisorFabrica) && $d->impuestoAsumidoEmisorFabrica != "") {
- $xmlString .= '' . $d->impuestoAsumidoEmisorFabrica . '';
- }
- $xmlString .= '' . $d->impuestoNeto . '';
- $xmlString .= '' . $d->montoTotalLinea . '';
- $xmlString .= '';
- $l++;
- }
-
- $xmlString .= '';
-
- //OtrosCargos
- if (isset($otrosCargos) && $otrosCargos != "") {
- foreach ($otrosCargos as $o) {
- $xmlString .= '
-
- ' . $o->tipoDocumentoOC . '';
- if (isset($o->tipoDocumentoOTROS) && $o->tipoDocumentoOTROS != "") {
- $xmlString .= '
- ' . $o->tipoDocumentoOTROS . '';
- }
- if (isset($o->numeroIdentidadTercero) && $o->numeroIdentidadTercero != "" && isset($o->tipoIdentidadTercero) && $o->tipoIdentidadTercero != "") {
- $xmlString .= '
-
- ' . $o->tipoIdentidadTercero . '
- ' . $o->numeroIdentidadTercero . '
- ';
- }
- if (isset($o->nombreTercero) && $o->nombreTercero != "") {
- $xmlString .= '
- ' . $o->nombreTercero . '';
- }
- $xmlString .= '
- ' . $o->detalle . '';
- if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
- $xmlString .= '
- ' . $o->porcentajeOC . '';
- }
- $xmlString .= '
- ' . $o->montoCargo . '';
- $xmlString .= '
- ';
- }
- }
-
- $xmlString .= '
- ';
-
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
- ' . $codMoneda . '
- ' . $tipoCambio . '
- ';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalServExonerados != '') {
- $xmlString .= '
- ' . $totalServExonerados . '';
- }
-
- if ($totalServNoSujeto != '') {
- $xmlString .= '
- ' . $totalServNoSujeto . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalMercExonerada != '') {
- $xmlString .= '
- ' . $totalMercExonerada . '';
- }
-
- if ($totalMercNoSujeta != '') {
- $xmlString .= '
- ' . $totalMercNoSujeta . '';
- }
-
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
-
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
-
- if ($totalExonerado != '') {
- $xmlString .= '
- ' . $totalExonerado . '';
- }
-
- if ($totalNoSujeto != '') {
- $xmlString .= '
- ' . $totalNoSujeto . '';
- }
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
- }
-
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
- }
- $xmlString .= '';
- }
- }
-
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
- }
-
- if ($totalImpAsumidoEmisorFabrica != '') {
- $xmlString .= '
- ' . $totalImpAsumidoEmisorFabrica . '';
- }
-
- if ($totalIVADevuelto != '') {
- $xmlString .= '
- ' . $totalIVADevuelto . '';
- }
-
- if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
- $xmlString .= '
- ' . $totalOtrosCargos . '';
- }
-
- if (isset($mediosPago) && !empty($mediosPago)) {
- foreach ($mediosPago as $o) {
- $xmlString .= '
- ';
-
- // Add TipoMedioPago
- if (isset($o->tipoMedioPago) && !empty($o->tipoMedioPago)) {
- $xmlString .= '' . $o->tipoMedioPago . '';
- }
-
- // Add MedioPagoOtros (only if TipoMedioPago is "99")
- if (isset($o->tipoMedioPago) && $o->tipoMedioPago === "99" && isset($o->medioPagoOtros) && !empty($o->medioPagoOtros)) {
- $xmlString .= '' . htmlspecialchars($o->medioPagoOtros) . '';
- }
-
- // Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
- }
-
- $xmlString .= '';
- }
- }
-
- $xmlString .= '
- ' . $totalComprobante . '
- ';
-
- if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
- foreach ($informacionReferencia as $ref) {
- if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
- if (in_array($ref->tipoDoc, TIPODOCREFVALUES, true)) {
- $xmlString .= '';
- $xmlString .= '' . $ref->tipoDoc . '';
- if ($ref->tipoDoc === '99' && isset($ref->tipoDocOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->tipoDocOtro) . '';
- }
- if (isset($ref->numero)) {
- $xmlString .= '' . $ref->numero . '';
- }
- $xmlString .= '' . $ref->fechaEmision . '';
- if (isset($ref->codigo)) {
- $xmlString .= '' . $ref->codigo . '';
- if ($ref->codigo === '99' && isset($ref->codigoOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->codigoOtro) . '';
- }
- }
- if (isset($ref->razon)) {
- $xmlString .= '' . $ref->razon . '';
- }
- $xmlString .= '';
- } else {
- grace_error("El parámetro tipoDoc no cumple con la estructura establecida. tipoDoc = " . $ref->tipoDoc);
- }
- }
- }
- }
-
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
- // Start Otros element
- $xmlString .= '';
-
- // Handle multiple OtroTexto elements
- if (isset($otros->otroTexto)) {
- if (is_array($otros->otroTexto)) {
- foreach ($otros->otroTexto as $otroTexto) {
- $codigo = isset($otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otroTexto->codigo) . '"' : '';
- $texto = isset($otroTexto->texto) ? htmlspecialchars($otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- } else {
- $codigo = isset($otros->otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otros->otroTexto->codigo) . '"' : '';
- $texto = isset($otros->otroTexto->texto) ? htmlspecialchars($otros->otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- }
-
- // Handle multiple OtroContenido elements
- if (isset($otros->otroContenido) && is_array($otros->otroContenido)) {
- foreach ($otros->otroContenido as $otroContenido) {
- $codigo = isset($otroContenido->codigo) ? ' codigo="' . htmlspecialchars($otroContenido->codigo) . '"' : '';
- $contenido = '';
- if (isset($otroContenido->contenidoEstructurado) && is_object($otroContenido->contenidoEstructurado)) {
- foreach ($otroContenido->contenidoEstructurado as $tag => $data) {
- $contenido .= '<' . $tag . '>';
- if (is_object($data)) {
- foreach ($data as $k => $v) {
- $contenido .= '<' . $k . '>' . htmlspecialchars($v) . '' . $k . '>';
- }
- }
- $contenido .= '' . $tag . '>';
- }
- }
- $xmlString .= '' . $contenido . '';
- }
- }
-
- $xmlString .= '';
-
- // XML Resultante
- //
- // Texto opcional 1
- //
- //
- // developer@example.com
- // Developer Name
- // +123456789
- //
- //
- //
- //
- // support@example.com
- // Support Team
- // +987654321
- //
- //
- //
-
- $xmlString .= '
- ';
-
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-function genXMLND()
-{
-
- // Datos contribuyente
- $clave = params_get("clave");
- $proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $codigoActividadReceptor = params_get("codigo_actividad_receptor");
- $consecutivo = params_get("consecutivo");
- $fechaEmision = params_get("fecha_emision");
-
- // Datos emisor
- $emisorNombre = params_get("emisor_nombre");
- $emisorTipoIdentif = params_get("emisor_tipo_identif");
- $emisorNumIdentif = params_get("emisor_num_identif");
- $emisorNombreComercial = params_get("emisor_nombre_comercial");
- $emisorProv = params_get("emisor_provincia");
- $emisorCanton = params_get("emisor_canton");
- $emisorDistrito = params_get("emisor_distrito");
- $emisorBarrio = params_get("emisor_barrio");
- $emisorOtrasSenas = params_get("emisor_otras_senas");
- $emisorCodPaisTel = params_get("emisor_cod_pais_tel");
- $emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email");
- $registroFiscal8707 = params_get("registrofiscal8707");
-
- // Datos receptor
- $omitir_receptor = params_get("omitir_receptor"); // Deprecated
- $receptorNombre = params_get("receptor_nombre");
- $receptorTipoIdentif = params_get("receptor_tipo_identif");
- $receptorNumIdentif = params_get("receptor_num_identif");
- $receptorIdentifExtranjero = params_get("receptor_identif_extranjero");
- $receptorNombreComercial = params_get("receptor_nombre_comercial");
- $receptorProvincia = params_get("receptor_provincia");
- $receptorCanton = params_get("receptor_canton");
- $receptorDistrito = params_get("receptor_distrito");
- $receptorBarrio = params_get("receptor_barrio");
- $receptorOtrasSenas = params_get("receptor_otras_senas");
- $receptorOtrasSenasExtranjero = params_get("receptor_otras_senas_extranjero");
- $receptorCodPaisTel = params_get("receptor_cod_pais_tel");
- $receptorTel = params_get("receptor_tel");
- $receptorEmail = params_get("receptor_email");
-
- // Detalles de tiquete / Factura
- $condVenta = params_get("condicion_venta");
- $condVentaOtros = params_get("condicion_venta_otros");
- $plazoCredito = params_get("plazo_credito");
- $codMoneda = params_get("cod_moneda");
- $tipoCambio = params_get("tipo_cambio");
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalServExonerados = params_get("total_serv_exonerados");
- $totalServNoSujeto = params_get("total_serv_no_sujeto");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalMercExonerada = params_get("total_merc_exonerada");
- $totalMercNoSujeta = params_get("total_merc_no_sujeta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalExonerado = params_get("total_exonerado");
- $totalNoSujeto = params_get("total_no_sujeto");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
- $totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
- $totalIVADevuelto = params_get("totalIVADevuelto");
- $totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
- $otros = json_decode(params_get('otros'));
-
- // Detalles de la compra
- $detalles = json_decode(params_get("detalles"));
- $informacionReferencia = json_decode(params_get("informacion_referencia"));
- $otrosCargos = json_decode(params_get("otrosCargos"));
- $mediosPago = json_decode(params_get("medios_pago"));
- // Resumen
- $totalDesgloseImpuesto = json_decode(params_get("totalDesgloseImpuesto"));
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- grace_debug(params_get("otrosCargos"));
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- grace_debug(params_get("medios_pago"));
- }
-
- if (isset($totalDesgloseImpuesto) && $totalDesgloseImpuesto != "") {
- grace_debug(params_get("totalDesgloseImpuesto"));
- }
-
- // Validate string sizes
- $codigoActividadEmisor = str_pad($codigoActividadEmisor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadEmisor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadEmisor is " . $codigoActividadEmisor);
- }
-
- if (strlen($emisorNombre) > EMISORNOMBREMAXSIZE) {
- error_log("emisorNombreSize: " . EMISORNOMBREMAXSIZE . " is greater than emisorNombre: " . $emisorNombre);
- }
-
- if (strlen($receptorNombre) > RECEPTORNOMBREMAXSIZE) {
- error_log("receptorNombreMaxSize: " . RECEPTORNOMBREMAXSIZE . " is greater than receptorNombre: " . $receptorNombre);
- }
-
- if (strlen($receptorOtrasSenas) > RECEPTOROTRASSENASMAXSIZE) {
- error_log("RECEPTOROTRASSENASEXTRANJEROMAXSIZE: " . RECEPTOROTRASSENASMAXSIZE . " is greater than receptorOtrasSenas: " . $receptorOtrasSenas);
- }
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- if (count($otrosCargos) > 15) {
- error_log("otrosCargos: " . count($otrosCargos) . " is greater than 15");
- //Delimita el array a solo 15 elementos
- $otrosCargos = array_slice($otrosCargos, 0, 15);
- }
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- if (count($mediosPago) > 4) {
- error_log("medios_pago: " . count($mediosPago) . " is greater than 4");
- //Delimita el array a solo 4 elementos
- $mediosPago = array_slice($mediosPago, 0, 4);
- }
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $proveedorSistemas . '
- ' . $codigoActividadEmisor . '';
-
- if (isset($codigoActividadReceptor) && $codigoActividadReceptor != "") {
- $codigoActividadReceptor = str_pad($codigoActividadReceptor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadReceptor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadReceptor is " . $codigoActividadReceptor);
- }
-
- $xmlString .= '
- ' . $codigoActividadReceptor . '';
- }
-
- $xmlString .= '
- ' . $consecutivo . '
- ' . $fechaEmision . '
-
- ' . $emisorNombre . '
-
- ' . $emisorTipoIdentif . '
- ' . $emisorNumIdentif . '
- ';
-
- if (isset($registroFiscal8707) && $registroFiscal8707 != "") {
- $xmlString .= '
- ' . $registroFiscal8707 . '';
- }
-
- if (isset($emisorNombreComercial) && $emisorNombreComercial != "") {
- $xmlString .= '
- ' . $emisorNombreComercial . '';
- }
-
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $emisorProv . '
- ' . $emisorCanton . '
- ' . $emisorDistrito . '';
- if ($emisorBarrio != '') {
- $xmlString .= '' . $emisorBarrio . '';
- }
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
- }
-
- if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
- $xmlString .= '
-
- ' . $emisorCodPaisTel . '
- ' . $emisorTel . '
- ';
- }
-
- if (preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
- } else {
- error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
- }
-
- if ($omitir_receptor != 'true') {
- $xmlString .= '
- ' . $receptorNombre . '';
-
- if ($receptorTipoIdentif != '' && $receptorNumIdentif != '') {
- $xmlString .= '
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
- }
-
- if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
- $xmlString .= '
- '
- . $receptorIdentifExtranjero .
- '';
- }
-
- if (isset($receptorNombreComercial) && $receptorNombreComercial != "") {
- $xmlString .= '
- ' . $receptorNombreComercial . '';
- }
-
- if ($receptorProvincia != '' && $receptorCanton != '' && $receptorDistrito != '' && $receptorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $receptorProvincia . '
- ' . $receptorCanton . '
- ' . $receptorDistrito . '';
- if ($receptorBarrio != '') {
- $xmlString .= '' . $receptorBarrio . '';
- }
- $xmlString .= '
- ' . $receptorOtrasSenas . '
- ';
- }
-
- if ($receptorOtrasSenasExtranjero != '' && strlen($receptorOtrasSenasExtranjero) <= RECEPTOROTRASSENASEXTRANJEROMAXSIZE) {
- $xmlString .= '
- '
- . $receptorOtrasSenasExtranjero .
- '';
- }
-
- if ($receptorCodPaisTel != '' && $receptorTel != '') {
- $xmlString .= '
-
- ' . $receptorCodPaisTel . '
- ' . $receptorTel . '
- ';
- }
-
- if ($receptorEmail != '') {
- $xmlString .= '' . $receptorEmail . '';
- }
-
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $condVenta . '';
-
- if (isset($condVentaOtros) && $condVentaOtros != "") {
- $xmlString .= '
- ' . $condVentaOtros . '';
- }
-
- if (isset($plazoCredito) && $plazoCredito != "") {
- $xmlString .= '
- ' . $plazoCredito . '';
- }
-
- $xmlString .= '
- ';
-
- /* EJEMPLO DE DETALLES
- {
- "1":["1","Sp","Honorarios","100000","100000","100000","100000","1000","Pronto pago",{"Imp": [{"cod": 122,"tarifa": 1,"monto": 100},{"cod": 133,"tarifa": 1,"monto": 1300}]}],
- "2":["1","Sp","Honorarios","100000","100000","100000","100000"]
- }
- */
-
- $l = 1;
- foreach ($detalles as $d) {
- $xmlString .= '
-
- ' . $l . '';
- if (isset($d->partidaArancelaria) && $d->partidaArancelaria != "") {
- $xmlString .= '' . $d->partidaArancelaria . '';
- }
-
- if (isset($d->codigoCABYS) && $d->codigoCABYS != "") {
- $xmlString .= '
- ' . $d->codigoCABYS . '';
- }
-
- if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
- // Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
-
- // Delimitar el array a solo 5 elementos
- if (count($codigoComercialArray) > 5) {
- error_log("codigoComercial: " . count($codigoComercialArray) . " is greater than 5");
- }
- $codigoComercialArray = array_slice($codigoComercialArray, 0, 5);
-
- // Iterar sobre los elementos del array
- foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
- // Verificar si el elemento es un array asociativo
- if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
- $xmlString .= '
-
- ' . $c['tipo'] . '
- ' . $c['codigo'] . '
- ';
- }
- }
- }
-
- $xmlString .= '
- ' . $d->cantidad . '
- ' . $d->unidadMedida . '';
- if (isset($d->tipoTransaccion) && $d->tipoTransaccion != "") {
- $xmlString .= '
- ' . $d->tipoTransaccion . '';
- }
- if (isset($d->unidadMedidaComercial) && $d->unidadMedidaComercial != "") {
- $xmlString .= '
- ' . $d->unidadMedidaComercial . '';
- }
- $xmlString .= '
- ' . $d->detalle . '';
- if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
- $xmlString .= '' . $d->numeroVINoSerie . '';
- }
-
- if (isset($d->registroMedicamento) && $d->registroMedicamento !== "") {
- $xmlString .= '' . htmlspecialchars($d->registroMedicamento) . '';
- }
- if (isset($d->formaFarmaceutica) && $d->formaFarmaceutica !== "") {
- $xmlString .= '' . htmlspecialchars($d->formaFarmaceutica) . '';
- }
-
- if (isset($d->detalleSurtido) && is_array($d->detalleSurtido) && count($d->detalleSurtido) > 0) {
- $xmlString .= '';
- $lineas = array_slice($d->detalleSurtido, 0, 20);
- foreach ($lineas as $linea) {
- $xmlString .= '';
- $xmlString .= '' . $linea->codigoCABYSSurtido . '';
- if (isset($linea->codigoComercialSurtido) && is_array($linea->codigoComercialSurtido)) {
- $codigos = array_slice($linea->codigoComercialSurtido, 0, 5);
- foreach ($codigos as $codigo) {
- $xmlString .= '';
- $xmlString .= '' . $codigo->tipoSurtido . '';
- $xmlString .= '' . $codigo->codigoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->cantidadSurtido . '';
- $xmlString .= '' . $linea->unidadMedidaSurtido . '';
- if (isset($linea->unidadMedidaComercialSurtido)) {
- $xmlString .= '' . $linea->unidadMedidaComercialSurtido . '';
- }
- $xmlString .= '' . $linea->detalleSurtido . '';
- $xmlString .= '' . $linea->precioUnitarioSurtido . '';
- $xmlString .= '' . $linea->montoTotalSurtido . '';
- if (isset($linea->descuentoSurtido) && is_array($linea->descuentoSurtido)) {
- $descuentos = array_slice($linea->descuentoSurtido, 0, 5);
- foreach ($descuentos as $desc) {
- $xmlString .= '';
- $xmlString .= '' . $desc->montoDescuentoSurtido . '';
- $xmlString .= '' . $desc->codigoDescuentoSurtido . '';
- if (isset($desc->descuentoSurtidoOtros)) {
- $xmlString .= '' . $desc->descuentoSurtidoOtros . '';
- }
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->subTotalSurtido . '';
- if (isset($linea->ivaCobradoFabricaSurtido)) {
- $xmlString .= '' . $linea->ivaCobradoFabricaSurtido . '';
- }
- $xmlString .= '' . $linea->baseImponibleSurtido . '';
- if (isset($linea->impuestoSurtido) && is_array($linea->impuestoSurtido)) {
- $impuestos = array_slice($linea->impuestoSurtido, 0, 1000);
- foreach ($impuestos as $imp) {
- $xmlString .= '';
- $xmlString .= '' . $imp->codigoImpuestoSurtido . '';
- if (isset($imp->codigoImpuestoOTROSurtido)) {
- $xmlString .= '' . $imp->codigoImpuestoOTROSurtido . '';
- }
- if (isset($imp->codigoTarifaIVASurtido)) {
- $xmlString .= '' . $imp->codigoTarifaIVASurtido . '';
- }
- if (isset($imp->tarifaSurtido)) {
- $xmlString .= '' . $imp->tarifaSurtido . '';
- }
- if (isset($imp->datosImpuestoEspecificoSurtido)) {
- $e = $imp->datosImpuestoEspecificoSurtido;
- $xmlString .= '';
- if (isset($e->cantidadUnidadMedidaSurtido)) {
- $xmlString .= '' . $e->cantidadUnidadMedidaSurtido . '';
- }
- if (isset($e->porcentajeSurtido)) {
- $xmlString .= '' . $e->porcentajeSurtido . '';
- }
- if (isset($e->proporcionSurtido)) {
- $xmlString .= '' . $e->proporcionSurtido . '';
- }
- if (isset($e->volumenUnidadConsumoSurtido)) {
- $xmlString .= '' . $e->volumenUnidadConsumoSurtido . '';
- }
- if (isset($e->impuestoUnidadSurtido)) {
- $xmlString .= '' . $e->impuestoUnidadSurtido . '';
- }
- $xmlString .= '';
- }
- $xmlString .= '' . $imp->montoImpuestoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
- if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
-
- if (count($descuentoArray) > 5) {
- error_log("descuento: " . count($descuentoArray) . " is greater than 5");
- }
- $descuentoArray = array_slice($descuentoArray, 0, 5);
-
- foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
- if (
- is_array($c) &&
- isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
- isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
- ) {
- $xmlString .= '
-
- ' . $c['montoDescuento'] . '
- ' . $c['codigoDescuento'] . '';
- // CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
- if (
- isset($c['codigoDescuento']) && $c['codigoDescuento'] === "99" &&
- isset($c['codigoDescuentoOTRO']) &&
- strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
- ) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
- }
- // NaturalezaDescuento: minOccurs=0, longitud 3-80
- if (
- isset($c['naturalezaDescuento']) &&
- strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
- ) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
- }
- $xmlString .= '
- ';
- }
- }
- }
-
- $xmlString .= '' . $d->subTotal . '';
-
- if (isset($d->IVACobradoFabrica) && $d->IVACobradoFabrica != "") {
- $xmlString .= '' . $d->IVACobradoFabrica . '';
- }
-
- if (isset($d->baseImponible) && $d->baseImponible != "") {
- $xmlString .= '' . $d->baseImponible . '';
- }
- if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
- $xmlString .= '
- ' . $i->codigo . '';
-
- // Add if required
- if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
- ) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
- }
-
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '' . $i->codigoTarifa . '';
- }
-
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '' . $i->tarifa . '';
- }
-
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '' . $i->factorIVA . '';
- }
-
- if (
- isset($i->codigo) &&
- in_array($i->codigo, ["03", "04", "05", "06"]) &&
- isset($i->datosImpuestoEspecifico) &&
- is_object($i->datosImpuestoEspecifico)
- ) {
- $datosImpuestoEsp = $i->datosImpuestoEspecifico;
- $xmlString .= '';
- if (isset($datosImpuestoEsp->cantidadUnidadMedida)) {
- $xmlString .= '' . $datosImpuestoEsp->cantidadUnidadMedida . '';
- }
- if (isset($datosImpuestoEsp->porcentaje)) {
- $xmlString .= '' . $datosImpuestoEsp->porcentaje . '';
- }
- if (isset($datosImpuestoEsp->proporcion)) {
- $xmlString .= '' . $datosImpuestoEsp->proporcion . '';
- }
- if (isset($datosImpuestoEsp->volumenUnidadConsumo)) {
- $xmlString .= '' . $datosImpuestoEsp->volumenUnidadConsumo . '';
- }
- if (isset($datosImpuestoEsp->impuestoUnidad)) {
- $xmlString .= '' . $datosImpuestoEsp->impuestoUnidad . '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '' . $i->monto . '';
-
- if (isset($i->montoExportacion) && $i->montoExportacion != "") {
- $xmlString .= '' . $i->montoExportacion . '';
- }
-
- if (isset($i->exoneracion) && $i->exoneracion != "") {
- $xmlString .= '
-
- ' . $i->exoneracion->tipoDocumento . '';
- if (isset($i->exoneracion->tipoDocumentoOtro) && !empty($i->exoneracion->tipoDocumentoOtro)) {
- $xmlString .= '' . $i->exoneracion->tipoDocumentoOtro . '';
- }
- $xmlString .= '' . $i->exoneracion->numeroDocumento . '';
- if (isset($i->exoneracion->numeroArticulo) && !empty($i->exoneracion->numeroArticulo)) {
- $xmlString .= '' . $i->exoneracion->numeroArticulo . '';
- }
- if (isset($i->exoneracion->numeroInciso) && !empty($i->exoneracion->numeroInciso)) {
- $xmlString .= '' . $i->exoneracion->numeroInciso . '';
- }
- $xmlString .= '' . $i->exoneracion->nombreInstitucion . '';
- if (isset($i->exoneracion->nombreInstitucionOtros) && !empty($i->exoneracion->nombreInstitucionOtros)) {
- $xmlString .= '' . $i->exoneracion->nombreInstitucionOtros . '';
- }
- $xmlString .= '
- ' . $i->exoneracion->fechaEmision . '
- ' . $i->exoneracion->tarifaExoneracion . '
- ' . $i->exoneracion->montoExoneracion . '
- ';
- }
-
- $xmlString .= '';
- }
- }
- if (isset($d->impuestoAsumidoEmisorFabrica) && $d->impuestoAsumidoEmisorFabrica != "") {
- $xmlString .= '' . $d->impuestoAsumidoEmisorFabrica . '';
- }
- $xmlString .= '' . $d->impuestoNeto . '';
- $xmlString .= '' . $d->montoTotalLinea . '';
- $xmlString .= '';
- $l++;
- }
-
- $xmlString .= '';
-
- //OtrosCargos
- if (isset($otrosCargos) && $otrosCargos != "") {
- foreach ($otrosCargos as $o) {
- $xmlString .= '
-
- ' . $o->tipoDocumentoOC . '';
- if (isset($o->tipoDocumentoOTROS) && $o->tipoDocumentoOTROS != "") {
- $xmlString .= '
- ' . $o->tipoDocumentoOTROS . '';
- }
- if (isset($o->numeroIdentidadTercero) && $o->numeroIdentidadTercero != "" && isset($o->tipoIdentidadTercero) && $o->tipoIdentidadTercero != "") {
- $xmlString .= '
-
- ' . $o->tipoIdentidadTercero . '
- ' . $o->numeroIdentidadTercero . '
- ';
- }
- if (isset($o->nombreTercero) && $o->nombreTercero != "") {
- $xmlString .= '
- ' . $o->nombreTercero . '';
- }
- $xmlString .= '
- ' . $o->detalle . '';
- if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
- $xmlString .= '
- ' . $o->porcentajeOC . '';
- }
- $xmlString .= '
- ' . $o->montoCargo . '';
- $xmlString .= '
- ';
- }
- }
-
- $xmlString .= '
- ';
-
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
- ' . $codMoneda . '
- ' . $tipoCambio . '
- ';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalServExonerados != '') {
- $xmlString .= '
- ' . $totalServExonerados . '';
- }
-
- if ($totalServNoSujeto != '') {
- $xmlString .= '
- ' . $totalServNoSujeto . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalMercExonerada != '') {
- $xmlString .= '
- ' . $totalMercExonerada . '';
- }
-
- if ($totalMercNoSujeta != '') {
- $xmlString .= '
- ' . $totalMercNoSujeta . '';
- }
-
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
-
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
-
- if ($totalExonerado != '') {
- $xmlString .= '
- ' . $totalExonerado . '';
- }
-
- if ($totalNoSujeto != '') {
- $xmlString .= '
- ' . $totalNoSujeto . '';
- }
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
- }
-
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
- }
- $xmlString .= '';
- }
- }
-
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
- }
-
- if ($totalImpAsumidoEmisorFabrica != '') {
- $xmlString .= '
- ' . $totalImpAsumidoEmisorFabrica . '';
- }
-
- if ($totalIVADevuelto != '') {
- $xmlString .= '
- ' . $totalIVADevuelto . '';
- }
-
- if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
- $xmlString .= '
- ' . $totalOtrosCargos . '';
- }
-
- if (isset($mediosPago) && !empty($mediosPago)) {
- foreach ($mediosPago as $o) {
- $xmlString .= '
- ';
-
- // Add TipoMedioPago
- if (isset($o->tipoMedioPago) && !empty($o->tipoMedioPago)) {
- $xmlString .= '' . $o->tipoMedioPago . '';
- }
-
- // Add MedioPagoOtros (only if TipoMedioPago is "99")
- if (isset($o->tipoMedioPago) && $o->tipoMedioPago === "99" && isset($o->medioPagoOtros) && !empty($o->medioPagoOtros)) {
- $xmlString .= '' . htmlspecialchars($o->medioPagoOtros) . '';
- }
-
- // Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
- }
-
- $xmlString .= '';
- }
- }
-
- $xmlString .= '
- ' . $totalComprobante . '
- ';
-
- if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
- foreach ($informacionReferencia as $ref) {
- if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
- if (in_array($ref->tipoDoc, TIPODOCREFVALUES, true)) {
- $xmlString .= '';
- $xmlString .= '' . $ref->tipoDoc . '';
- if ($ref->tipoDoc === '99' && isset($ref->tipoDocOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->tipoDocOtro) . '';
- }
- if (isset($ref->numero)) {
- $xmlString .= '' . $ref->numero . '';
- }
- $xmlString .= '' . $ref->fechaEmision . '';
- if (isset($ref->codigo)) {
- $xmlString .= '' . $ref->codigo . '';
- if ($ref->codigo === '99' && isset($ref->codigoOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->codigoOtro) . '';
- }
- }
- if (isset($ref->razon)) {
- $xmlString .= '' . $ref->razon . '';
- }
- $xmlString .= '';
- } else {
- grace_error("El parámetro tipoDoc no cumple con la estructura establecida. tipoDoc = " . $ref->tipoDoc);
- }
- }
- }
- }
-
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
- // Start Otros element
- $xmlString .= '';
-
- // Handle multiple OtroTexto elements
- if (isset($otros->otroTexto)) {
- if (is_array($otros->otroTexto)) {
- foreach ($otros->otroTexto as $otroTexto) {
- $codigo = isset($otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otroTexto->codigo) . '"' : '';
- $texto = isset($otroTexto->texto) ? htmlspecialchars($otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- } else {
- $codigo = isset($otros->otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otros->otroTexto->codigo) . '"' : '';
- $texto = isset($otros->otroTexto->texto) ? htmlspecialchars($otros->otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- }
-
- // Handle multiple OtroContenido elements
- if (isset($otros->otroContenido) && is_array($otros->otroContenido)) {
- foreach ($otros->otroContenido as $otroContenido) {
- $codigo = isset($otroContenido->codigo) ? ' codigo="' . htmlspecialchars($otroContenido->codigo) . '"' : '';
- $contenido = '';
- if (isset($otroContenido->contenidoEstructurado) && is_object($otroContenido->contenidoEstructurado)) {
- foreach ($otroContenido->contenidoEstructurado as $tag => $data) {
- $contenido .= '<' . $tag . '>';
- if (is_object($data)) {
- foreach ($data as $k => $v) {
- $contenido .= '<' . $k . '>' . htmlspecialchars($v) . '' . $k . '>';
- }
- }
- $contenido .= '' . $tag . '>';
- }
- }
- $xmlString .= '' . $contenido . '';
- }
- }
-
- $xmlString .= '';
-
- // XML Resultante
- //
- // Texto opcional 1
- //
- //
- // developer@example.com
- // Developer Name
- // +123456789
- //
- //
- //
- //
- // support@example.com
- // Support Team
- // +987654321
- //
- //
- //
-
- $xmlString .= '
- ';
-
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-function genXMLTE()
-{
-
- // Datos contribuyente
- $clave = params_get("clave");
- $proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $consecutivo = params_get("consecutivo");
- $fechaEmision = params_get("fecha_emision");
-
- // Datos emisor
- $emisorNombre = params_get("emisor_nombre");
- $emisorTipoIdentif = params_get("emisor_tipo_identif");
- $emisorNumIdentif = params_get("emisor_num_identif");
- $emisorNombreComercial = params_get("emisor_nombre_comercial");
- $emisorProv = params_get("emisor_provincia");
- $emisorCanton = params_get("emisor_canton");
- $emisorDistrito = params_get("emisor_distrito");
- $emisorBarrio = params_get("emisor_barrio");
- $emisorOtrasSenas = params_get("emisor_otras_senas");
- $emisorCodPaisTel = params_get("emisor_cod_pais_tel");
- $emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email");
- $registroFiscal8707 = params_get("registrofiscal8707");
-
- // Datos receptor
- $omitir_receptor = params_get("omitir_receptor"); // Deprecated
- $receptorNombre = params_get("receptor_nombre");
- $receptorTipoIdentif = params_get("receptor_tipo_identif");
- $receptorNumIdentif = params_get("receptor_num_identif");
- $receptorIdentifExtranjero = params_get("receptor_identif_extranjero");
- $receptorNombreComercial = params_get("receptor_nombre_comercial");
- $receptorProvincia = params_get("receptor_provincia");
- $receptorCanton = params_get("receptor_canton");
- $receptorDistrito = params_get("receptor_distrito");
- $receptorBarrio = params_get("receptor_barrio");
- $receptorOtrasSenas = params_get("receptor_otras_senas");
- $receptorOtrasSenasExtranjero = params_get("receptor_otras_senas_extranjero");
- $receptorCodPaisTel = params_get("receptor_cod_pais_tel");
- $receptorTel = params_get("receptor_tel");
- $receptorEmail = params_get("receptor_email");
-
- // Detalles de tiquete / Factura
- $condVenta = params_get("condicion_venta");
- $condVentaOtros = params_get("condicion_venta_otros");
- $plazoCredito = params_get("plazo_credito");
- $codMoneda = params_get("cod_moneda");
- $tipoCambio = params_get("tipo_cambio");
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalServExonerados = params_get("total_serv_exonerados");
- $totalServNoSujeto = params_get("total_serv_no_sujeto");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalMercExonerada = params_get("total_merc_exonerada");
- $totalMercNoSujeta = params_get("total_merc_no_sujeta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalExonerado = params_get("total_exonerado");
- $totalNoSujeto = params_get("total_no_sujeto");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
- $totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
- $totalIVADevuelto = params_get("totalIVADevuelto");
- $totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
-
- $otros = json_decode(params_get('otros'));
-
- // Detalles de la compra
- $detalles = json_decode(params_get("detalles"));
- $informacionReferencia = json_decode(params_get("informacion_referencia"));
-
- $otrosCargos = json_decode(params_get("otrosCargos"));
- $mediosPago = json_decode(params_get("medios_pago"));
- // Resumen
- $totalDesgloseImpuesto = json_decode(params_get("totalDesgloseImpuesto"));
-
- grace_debug(params_get("detalles"));
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- grace_debug(params_get("otrosCargos"));
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- grace_debug(params_get("medios_pago"));
- }
-
- if (isset($totalDesgloseImpuesto) && $totalDesgloseImpuesto != "") {
- grace_debug(params_get("totalDesgloseImpuesto"));
- }
-
- // Validate string sizes
- $codigoActividadEmisor = str_pad($codigoActividadEmisor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadEmisor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadEmisor is " . $codigoActividadEmisor);
- }
-
- if (strlen($emisorNombre) > EMISORNOMBREMAXSIZE) {
- error_log("emisorNombreSize: " . EMISORNOMBREMAXSIZE . " is greater than emisorNombre: " . $emisorNombre);
- }
-
- if (strlen($receptorNombre) > RECEPTORNOMBREMAXSIZE) {
- error_log("receptorNombreMaxSize: " . RECEPTORNOMBREMAXSIZE . " is greater than receptorNombre: " . $receptorNombre);
- }
-
- if (strlen($receptorOtrasSenas) > RECEPTOROTRASSENASMAXSIZE) {
- error_log("RECEPTOROTRASSENASEXTRANJEROMAXSIZE: " . RECEPTOROTRASSENASMAXSIZE . " is greater than receptorOtrasSenas: " . $receptorOtrasSenas);
- }
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- if (count($otrosCargos) > 15) {
- error_log("otrosCargos: " . count($otrosCargos) . " is greater than 15");
- //Delimita el array a solo 15 elementos
- $otrosCargos = array_slice($otrosCargos, 0, 15);
- }
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- if (count($mediosPago) > 4) {
- error_log("medios_pago: " . count($mediosPago) . " is greater than 4");
- //Delimita el array a solo 4 elementos
- $mediosPago = array_slice($mediosPago, 0, 4);
- }
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $proveedorSistemas . '
- ' . $codigoActividadEmisor . '
- ' . $consecutivo . '
- ' . $fechaEmision . '
-
- ' . $emisorNombre . '
-
- ' . $emisorTipoIdentif . '
- ' . $emisorNumIdentif . '
- ';
-
- if (isset($registroFiscal8707) && $registroFiscal8707 != "") {
- $xmlString .= '
- ' . $registroFiscal8707 . '';
- }
-
- if (isset($emisorNombreComercial) && $emisorNombreComercial != "") {
- $xmlString .= '
- ' . $emisorNombreComercial . '';
- }
-
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $emisorProv . '
- ' . $emisorCanton . '
- ' . $emisorDistrito . '';
- if ($emisorBarrio != '') {
- $xmlString .= '' . $emisorBarrio . '';
- }
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
- }
-
- if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
- $xmlString .= '
-
- ' . $emisorCodPaisTel . '
- ' . $emisorTel . '
- ';
- }
-
- if (preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
- } else {
- error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
- }
-
- if ($omitir_receptor != 'true') {
- $xmlString .= '
- ' . $receptorNombre . '';
-
- if ($receptorTipoIdentif != '' && $receptorNumIdentif != '') {
- $xmlString .= '
-
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
- }
-
- if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
- $xmlString .= '
- '
- . $receptorIdentifExtranjero .
- '';
- }
-
- if (isset($receptorNombreComercial) && $receptorNombreComercial != "") {
- $xmlString .= '
- ' . $receptorNombreComercial . '';
- }
-
- if ($receptorProvincia != '' && $receptorCanton != '' && $receptorDistrito != '' && $receptorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $receptorProvincia . '
- ' . $receptorCanton . '
- ' . $receptorDistrito . '';
- if ($receptorBarrio != '') {
- $xmlString .= '
- ' . $receptorBarrio . '';
- }
- $xmlString .= '
- ' . $receptorOtrasSenas . '
- ';
- }
-
- if ($receptorOtrasSenasExtranjero != '' && strlen($receptorOtrasSenasExtranjero) <= RECEPTOROTRASSENASEXTRANJEROMAXSIZE) {
- $xmlString .= '
- '
- . $receptorOtrasSenasExtranjero .
- '';
- }
-
- if ($receptorCodPaisTel != '' && $receptorTel != '') {
- $xmlString .= '
-
- ' . $receptorCodPaisTel . '
- ' . $receptorTel . '
- ';
- }
-
- if ($receptorEmail != '') {
- $xmlString .= '' . $receptorEmail . '';
- }
-
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $condVenta . '';
-
- if (isset($condVentaOtros) && $condVentaOtros != "") {
- $xmlString .= '
- ' . $condVentaOtros . '';
- }
-
- if (isset($plazoCredito) && $plazoCredito != "") {
- $xmlString .= '
- ' . $plazoCredito . '';
- }
-
- $xmlString .= '';
-
- // cant - unidad medida - detalle - precio unitario - monto total - subtotal - monto total linea - Monto desc -Naturaleza Desc - Impuesto : Codigo / Tarifa / Monto
-
- /* EJEMPLO DE DETALLES
- {
- "1":["1","Sp","Honorarios","100000","100000","100000","100000","1000","Pronto pago",{"Imp": [{"cod": 122,"tarifa": 1,"monto": 100},{"cod": 133,"tarifa": 1,"monto": 1300}]}],
- "2":["1","Sp","Honorarios","100000","100000","100000","100000"]
- }
- */
-
- $l = 1;
- foreach ($detalles as $d) {
- $xmlString .= '
-
- ' . $l . '';
-
- $xmlString .= '
- ' . $d->codigoCABYS . '';
-
- if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
- // Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
-
- // Delimitar el array a solo 5 elementos
- if (count($codigoComercialArray) > 5) {
- error_log("codigoComercial: " . count($codigoComercialArray) . " is greater than 5");
- }
- $codigoComercialArray = array_slice($codigoComercialArray, 0, 5);
-
- // Iterar sobre los elementos del array
- foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
- // Verificar si el elemento es un array asociativo
- if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
- $xmlString .= '
-
- ' . $c['tipo'] . '
- ' . $c['codigo'] . '
- ';
- }
- }
- }
-
- $xmlString .= '
- ' . $d->cantidad . '
- ' . $d->unidadMedida . '';
- if (isset($d->unidadMedidaComercial) && $d->unidadMedidaComercial != "") {
- $xmlString .= '
- ' . $d->unidadMedidaComercial . '';
- }
- $xmlString .= '
- ' . $d->detalle . '';
- if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
- $xmlString .= '' . $d->numeroVINoSerie . '';
- }
-
- if (isset($d->registroMedicamento) && $d->registroMedicamento !== "") {
- $xmlString .= '' . htmlspecialchars($d->registroMedicamento) . '';
- }
- if (isset($d->formaFarmaceutica) && $d->formaFarmaceutica !== "") {
- $xmlString .= '' . htmlspecialchars($d->formaFarmaceutica) . '';
- }
-
- if (isset($d->detalleSurtido) && is_array($d->detalleSurtido) && count($d->detalleSurtido) > 0) {
- $xmlString .= '';
- $lineas = array_slice($d->detalleSurtido, 0, 20);
- foreach ($lineas as $linea) {
- $xmlString .= '';
- $xmlString .= '' . $linea->codigoCABYSSurtido . '';
- if (isset($linea->codigoComercialSurtido) && is_array($linea->codigoComercialSurtido)) {
- $codigos = array_slice($linea->codigoComercialSurtido, 0, 5);
- foreach ($codigos as $codigo) {
- $xmlString .= '';
- $xmlString .= '' . $codigo->tipoSurtido . '';
- $xmlString .= '' . $codigo->codigoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->cantidadSurtido . '';
- $xmlString .= '' . $linea->unidadMedidaSurtido . '';
- if (isset($linea->unidadMedidaComercialSurtido)) {
- $xmlString .= '' . $linea->unidadMedidaComercialSurtido . '';
- }
- $xmlString .= '' . $linea->detalleSurtido . '';
- $xmlString .= '' . $linea->precioUnitarioSurtido . '';
- $xmlString .= '' . $linea->montoTotalSurtido . '';
- if (isset($linea->descuentoSurtido) && is_array($linea->descuentoSurtido)) {
- $descuentos = array_slice($linea->descuentoSurtido, 0, 5);
- foreach ($descuentos as $desc) {
- $xmlString .= '';
- $xmlString .= '' . $desc->montoDescuentoSurtido . '';
- $xmlString .= '' . $desc->codigoDescuentoSurtido . '';
- if (isset($desc->descuentoSurtidoOtros)) {
- $xmlString .= '' . $desc->descuentoSurtidoOtros . '';
- }
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->subTotalSurtido . '';
- if (isset($linea->ivaCobradoFabricaSurtido)) {
- $xmlString .= '' . $linea->ivaCobradoFabricaSurtido . '';
- }
- $xmlString .= '' . $linea->baseImponibleSurtido . '';
- if (isset($linea->impuestoSurtido) && is_array($linea->impuestoSurtido)) {
- $impuestos = array_slice($linea->impuestoSurtido, 0, 1000);
- foreach ($impuestos as $imp) {
- $xmlString .= '';
- $xmlString .= '' . $imp->codigoImpuestoSurtido . '';
- if (isset($imp->codigoImpuestoOTROSurtido)) {
- $xmlString .= '' . $imp->codigoImpuestoOTROSurtido . '';
- }
- if (isset($imp->codigoTarifaIVASurtido)) {
- $xmlString .= '' . $imp->codigoTarifaIVASurtido . '';
- }
- if (isset($imp->tarifaSurtido)) {
- $xmlString .= '' . $imp->tarifaSurtido . '';
- }
- if (isset($imp->datosImpuestoEspecificoSurtido)) {
- $e = $imp->datosImpuestoEspecificoSurtido;
- $xmlString .= '';
- if (isset($e->cantidadUnidadMedidaSurtido)) {
- $xmlString .= '' . $e->cantidadUnidadMedidaSurtido . '';
- }
- if (isset($e->porcentajeSurtido)) {
- $xmlString .= '' . $e->porcentajeSurtido . '';
- }
- if (isset($e->proporcionSurtido)) {
- $xmlString .= '' . $e->proporcionSurtido . '';
- }
- if (isset($e->volumenUnidadConsumoSurtido)) {
- $xmlString .= '' . $e->volumenUnidadConsumoSurtido . '';
- }
- if (isset($e->impuestoUnidadSurtido)) {
- $xmlString .= '' . $e->impuestoUnidadSurtido . '';
- }
- $xmlString .= '';
- }
- $xmlString .= '' . $imp->montoImpuestoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
- if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
-
- if (count($descuentoArray) > 5) {
- error_log("descuento: " . count($descuentoArray) . " is greater than 5");
- }
- $descuentoArray = array_slice($descuentoArray, 0, 5);
-
- foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
- if (
- is_array($c) &&
- isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
- isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
- ) {
- $xmlString .= '
-
- ' . $c['montoDescuento'] . '
- ' . $c['codigoDescuento'] . '';
- // CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
- if (
- isset($c['codigoDescuento']) && $c['codigoDescuento'] === "99" &&
- isset($c['codigoDescuentoOTRO']) &&
- strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
- ) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
- }
- // NaturalezaDescuento: minOccurs=0, longitud 3-80
- if (
- isset($c['naturalezaDescuento']) &&
- strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
- ) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
- }
- $xmlString .= '
- ';
- }
- }
- }
-
- $xmlString .= '' . $d->subTotal . '';
-
- if (isset($d->IVACobradoFabrica) && $d->IVACobradoFabrica != "") {
- $xmlString .= '' . $d->IVACobradoFabrica . '';
- }
-
- if (isset($d->baseImponible) && $d->baseImponible != "") {
- $xmlString .= '' . $d->baseImponible . '';
- }
- if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
- $xmlString .= '
-
- ' . $i->codigo . '';
-
- // Add if required
- if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
- ) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
- }
-
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '
- ' . $i->codigoTarifa . '';
- }
-
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '
- ' . $i->tarifa . '';
- }
-
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '
- ' . $i->factorIVA . '';
- }
-
- if (
- isset($i->codigo) &&
- in_array($i->codigo, ["03", "04", "05", "06"]) &&
- isset($i->datosImpuestoEspecifico) &&
- is_object($i->datosImpuestoEspecifico)
- ) {
- $datosImpuestoEsp = $i->datosImpuestoEspecifico;
- $xmlString .= '';
- if (isset($datosImpuestoEsp->cantidadUnidadMedida)) {
- $xmlString .= '' . $datosImpuestoEsp->cantidadUnidadMedida . '';
- }
- if (isset($datosImpuestoEsp->porcentaje)) {
- $xmlString .= '' . $datosImpuestoEsp->porcentaje . '';
- }
- if (isset($datosImpuestoEsp->proporcion)) {
- $xmlString .= '' . $datosImpuestoEsp->proporcion . '';
- }
- if (isset($datosImpuestoEsp->volumenUnidadConsumo)) {
- $xmlString .= '' . $datosImpuestoEsp->volumenUnidadConsumo . '';
- }
- if (isset($datosImpuestoEsp->impuestoUnidad)) {
- $xmlString .= '' . $datosImpuestoEsp->impuestoUnidad . '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $i->monto . '';
-
- if (isset($i->exoneracion) && $i->exoneracion != "") {
- $xmlString .= '
-
- ' . $i->exoneracion->tipoDocumento . '';
- if (isset($i->exoneracion->tipoDocumentoOtro) && !empty($i->exoneracion->tipoDocumentoOtro)) {
- $xmlString .= '' . $i->exoneracion->tipoDocumentoOtro . '';
- }
- $xmlString .= '' . $i->exoneracion->numeroDocumento . '';
- if (isset($i->exoneracion->numeroArticulo) && !empty($i->exoneracion->numeroArticulo)) {
- $xmlString .= '' . $i->exoneracion->numeroArticulo . '';
- }
- if (isset($i->exoneracion->numeroInciso) && !empty($i->exoneracion->numeroInciso)) {
- $xmlString .= '' . $i->exoneracion->numeroInciso . '';
- }
- $xmlString .= '' . $i->exoneracion->nombreInstitucion . '';
- if (isset($i->exoneracion->nombreInstitucionOtros) && !empty($i->exoneracion->nombreInstitucionOtros)) {
- $xmlString .= '' . $i->exoneracion->nombreInstitucionOtros . '';
- }
- $xmlString .= '
- ' . $i->exoneracion->fechaEmision . '
- ' . $i->exoneracion->tarifaExoneracion . '
- ' . $i->exoneracion->montoExoneracion . '
- ';
- }
-
- $xmlString .= '
- ';
- }
- }
- $xmlString .= '' . $d->impuestoAsumidoEmisorFabrica . '';
- $xmlString .= '' . $d->impuestoNeto . '';
- $xmlString .= '' . $d->montoTotalLinea . '';
- $xmlString .= '';
- $l++;
- }
-
- $xmlString .= '';
-
- //OtrosCargos
- if (isset($otrosCargos) && $otrosCargos != "") {
- foreach ($otrosCargos as $o) {
- $xmlString .= '
-
- ' . $o->tipoDocumentoOC . '';
- if (isset($o->tipoDocumentoOTROS) && $o->tipoDocumentoOTROS != "") {
- $xmlString .= '
- ' . $o->tipoDocumentoOTROS . '';
- }
- if (isset($o->numeroIdentidadTercero) && $o->numeroIdentidadTercero != "" && isset($o->tipoIdentidadTercero) && $o->tipoIdentidadTercero != "") {
- $xmlString .= '
-
- ' . $o->tipoIdentidadTercero . '
- ' . $o->numeroIdentidadTercero . '
- ';
- }
- if (isset($o->nombreTercero) && $o->nombreTercero != "") {
- $xmlString .= '
- ' . $o->nombreTercero . '';
- }
- $xmlString .= '
- ' . $o->detalle . '';
- if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
- $xmlString .= '
- ' . $o->porcentajeOC . '';
- }
- $xmlString .= '
- ' . $o->montoCargo . '';
- $xmlString .= '
- ';
- }
- }
-
- $xmlString .= '
- ';
-
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
- ' . $codMoneda . '
- ' . $tipoCambio . '
- ';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalServExonerados != '') {
- $xmlString .= '
- ' . $totalServExonerados . '';
- }
-
- if ($totalServNoSujeto != '') {
- $xmlString .= '
- ' . $totalServNoSujeto . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalMercExonerada != '') {
- $xmlString .= '
- ' . $totalMercExonerada . '';
- }
-
- if ($totalMercNoSujeta != '') {
- $xmlString .= '
- ' . $totalMercNoSujeta . '';
- }
-
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
-
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
-
- if ($totalExonerado != '') {
- $xmlString .= '
- ' . $totalExonerado . '';
- }
-
- if ($totalNoSujeto != '') {
- $xmlString .= '
- ' . $totalNoSujeto . '';
- }
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
- }
-
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
- }
- $xmlString .= '';
- }
- }
-
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
- }
-
- if ($totalImpAsumidoEmisorFabrica != '') {
- $xmlString .= '
- ' . $totalImpAsumidoEmisorFabrica . '';
- }
-
- if ($totalIVADevuelto != '') {
- $xmlString .= '
- ' . $totalIVADevuelto . '';
- }
-
- if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
- $xmlString .= '
- ' . $totalOtrosCargos . '';
- }
-
- if (isset($mediosPago) && !empty($mediosPago)) {
- foreach ($mediosPago as $o) {
- $xmlString .= '
- ';
-
- // Add TipoMedioPago
- if (isset($o->tipoMedioPago) && !empty($o->tipoMedioPago)) {
- $xmlString .= '' . $o->tipoMedioPago . '';
- }
-
- // Add MedioPagoOtros (only if TipoMedioPago is "99")
- if (isset($o->tipoMedioPago) && $o->tipoMedioPago === "99" && isset($o->medioPagoOtros) && !empty($o->medioPagoOtros)) {
- $xmlString .= '' . htmlspecialchars($o->medioPagoOtros) . '';
- }
-
- // Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
- }
-
- $xmlString .= '';
- }
- }
-
- $xmlString .= '
- ' . $totalComprobante . '
- ';
-
- if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
- foreach ($informacionReferencia as $ref) {
- if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
- if (in_array($ref->tipoDoc, TIPODOCREFVALUES, true)) {
- $xmlString .= '';
- $xmlString .= '' . $ref->tipoDoc . '';
- if ($ref->tipoDoc === '99' && isset($ref->tipoDocOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->tipoDocOtro) . '';
- }
- if (isset($ref->numero)) {
- $xmlString .= '' . $ref->numero . '';
- }
- $xmlString .= '' . $ref->fechaEmision . '';
- if (isset($ref->codigo)) {
- $xmlString .= '' . $ref->codigo . '';
- if ($ref->codigo === '99' && isset($ref->codigoOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->codigoOtro) . '';
- }
- }
- if (isset($ref->razon)) {
- $xmlString .= '' . $ref->razon . '';
- }
- $xmlString .= '';
- } else {
- grace_error("El parámetro tipoDoc no cumple con la estructura establecida. tipoDoc = " . $ref->tipoDoc);
- }
- }
- }
- }
-
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
- // Start Otros element
- $xmlString .= '';
-
- // Handle multiple OtroTexto elements
- if (isset($otros->otroTexto)) {
- if (is_array($otros->otroTexto)) {
- foreach ($otros->otroTexto as $otroTexto) {
- $codigo = isset($otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otroTexto->codigo) . '"' : '';
- $texto = isset($otroTexto->texto) ? htmlspecialchars($otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- } else {
- $codigo = isset($otros->otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otros->otroTexto->codigo) . '"' : '';
- $texto = isset($otros->otroTexto->texto) ? htmlspecialchars($otros->otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- }
-
- // Handle multiple OtroContenido elements
- if (isset($otros->otroContenido) && is_array($otros->otroContenido)) {
- foreach ($otros->otroContenido as $otroContenido) {
- $codigo = isset($otroContenido->codigo) ? ' codigo="' . htmlspecialchars($otroContenido->codigo) . '"' : '';
- $contenido = '';
- if (isset($otroContenido->contenidoEstructurado) && is_object($otroContenido->contenidoEstructurado)) {
- foreach ($otroContenido->contenidoEstructurado as $tag => $data) {
- $contenido .= '<' . $tag . '>';
- if (is_object($data)) {
- foreach ($data as $k => $v) {
- $contenido .= '<' . $k . '>' . htmlspecialchars($v) . '' . $k . '>';
- }
- }
- $contenido .= '' . $tag . '>';
- }
- }
- $xmlString .= '' . $contenido . '';
- }
- }
-
- $xmlString .= '';
-
- // XML Resultante
- //
- // Texto opcional 1
- //
- //
- // developer@example.com
- // Developer Name
- // +123456789
- //
- //
- //
- //
- // support@example.com
- // Support Team
- // +987654321
- //
- //
- //
-
- $xmlString .= '
- ';
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-function genXMLMr()
-{
-
- $clave = params_get("clave"); // d{50,50}
- // Datos vendedor = emisor
- $numeroCedulaEmisor = params_get("numero_cedula_emisor"); // d{12,12} cedula fisica,juridica,NITE,DIMEX
- $numeroCedulaEmisor = str_pad($numeroCedulaEmisor, 12, "0", STR_PAD_LEFT);
-
- // Datos mensaje receptor
- $fechaEmisionDoc = params_get("fecha_emision_doc"); // fecha de emision de la confirmacion
- $mensaje = params_get("mensaje"); // 1 - Aceptado, 2 - Aceptado Parcialmente, 3 - Rechazado
- $detalleMensaje = params_get("detalle_mensaje");
- $montoTotalImpuesto = params_get("monto_total_impuesto"); // d18,5 opcional /obligatorio si comprobante tenga impuesto
- $codigoActividad = params_get("codigo_actividad"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $totalFactura = params_get("total_factura"); // d18,5
- $numeroConsecutivoReceptor = params_get("numero_consecutivo_receptor"); // d{20,20} numeracion consecutiva de los mensajes de confirmacion
-
- // Datos comprador = receptor
- $numeroCedulaReceptor = params_get("numero_cedula_receptor"); // d{12,12}cedula fisica, juridica, NITE, DIMEX del comprador
- $numeroCedulaReceptor = str_pad($numeroCedulaReceptor, 12, "0", STR_PAD_LEFT);
-
- // Validate string sizes
- $codigoActividad = str_pad($codigoActividad, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividad) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize: " . CODIGOACTIVIDADSIZE . " is not codigoActividad: " . $codigoActividad);
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $numeroCedulaEmisor . '
- ' . $fechaEmisionDoc . '
- ' . $mensaje . '';
- if (!empty($detalleMensaje)) {
- $xmlString .= '' . $detalleMensaje . '';
- }
-
- if (!empty($montoTotalImpuesto)) {
- $xmlString .= '' . $montoTotalImpuesto . '';
- }
- $xmlString .= '' . $codigoActividad . '
- ' . $totalFactura . '
- ' . $numeroCedulaReceptor . '
- ' . $numeroConsecutivoReceptor . '';
-
- $xmlString .= '';
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-function genXMLFec()
-{
- // Datos contribuyente
- $clave = params_get("clave");
- $proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $codigoActividadReceptor = params_get("codigo_actividad_receptor");
- $consecutivo = params_get("consecutivo");
- $fechaEmision = params_get("fecha_emision");
-
- // Datos emisor
- $emisorNombre = params_get("emisor_nombre");
- $emisorTipoIdentif = params_get("emisor_tipo_identif");
- $emisorNumIdentif = params_get("emisor_num_identif");
- $emisorNombreComercial = params_get("emisor_nombre_comercial");
- $emisorProv = params_get("emisor_provincia");
- $emisorCanton = params_get("emisor_canton");
- $emisorDistrito = params_get("emisor_distrito");
- $emisorBarrio = params_get("emisor_barrio");
- $emisorOtrasSenas = params_get("emisor_otras_senas");
- $emisorOtrasSenasExtranjero = params_get("emisor_otras_senas_extranjero");
- $emisorCodPaisTel = params_get("emisor_cod_pais_tel");
- $emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email");
- $registroFiscal8707 = params_get("registrofiscal8707");
-
- // Datos receptor
- $omitir_receptor = params_get("omitir_receptor"); // Deprecated
- $receptorNombre = params_get("receptor_nombre");
- $receptorTipoIdentif = params_get("receptor_tipo_identif");
- $receptorNumIdentif = params_get("receptor_num_identif");
- $receptorIdentifExtranjero = params_get("receptor_identif_extranjero");
- $receptorNombreComercial = params_get("receptor_nombre_comercial");
- $receptorProvincia = params_get("receptor_provincia");
- $receptorCanton = params_get("receptor_canton");
- $receptorDistrito = params_get("receptor_distrito");
- $receptorBarrio = params_get("receptor_barrio");
- $receptorOtrasSenas = params_get("receptor_otras_senas");
- $receptorCodPaisTel = params_get("receptor_cod_pais_tel");
- $receptorTel = params_get("receptor_tel");
- $receptorEmail = params_get("receptor_email");
-
- // Detalles de tiquete / Factura
- $condVenta = params_get("condicion_venta");
- $condVentaOtros = params_get("condicion_venta_otros");
- $plazoCredito = params_get("plazo_credito");
- $codMoneda = params_get("cod_moneda");
- $tipoCambio = params_get("tipo_cambio");
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalServExonerados = params_get("total_serv_exonerados");
- $totalServNoSujeto = params_get("total_serv_no_sujeto");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalMercExonerada = params_get("total_merc_exonerada");
- $totalMercNoSujeta = params_get("total_merc_no_sujeta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalExonerado = params_get("total_exonerado");
- $totalNoSujeto = params_get("total_no_sujeto");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
- $totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
-
- $totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
- $otros = json_decode(params_get('otros'));
-
- // Detalles de la compra
- $detalles = json_decode(params_get("detalles"));
- $informacionReferencia = json_decode(params_get("informacion_referencia"));
-
- $otrosCargos = json_decode(params_get("otrosCargos"));
- $mediosPago = json_decode(params_get("medios_pago"));
- // Resumen
- $totalDesgloseImpuesto = json_decode(params_get("totalDesgloseImpuesto"));
-
- grace_debug(params_get("detalles"));
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- grace_debug(params_get("otrosCargos"));
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- grace_debug(params_get("medios_pago"));
- }
-
- if (isset($totalDesgloseImpuesto) && $totalDesgloseImpuesto != "") {
- grace_debug(params_get("totalDesgloseImpuesto"));
- }
-
- // Validate string sizes
- $codigoActividadEmisor = str_pad($codigoActividadEmisor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadEmisor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadEmisor is " . $codigoActividadEmisor);
- }
-
- $codigoActividadReceptor = str_pad($codigoActividadReceptor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadReceptor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadReceptor is " . $codigoActividadReceptor);
- }
-
- if (strlen($emisorNombre) > EMISORNOMBREMAXSIZE) {
- error_log("emisorNombreSize: " . EMISORNOMBREMAXSIZE . " is greater than emisorNombre: " . $emisorNombre);
- }
-
- if (strlen($receptorNombre) > RECEPTORNOMBREMAXSIZE) {
- error_log("receptorNombreMaxSize: " . RECEPTORNOMBREMAXSIZE . " is greater than receptorNombre: " . $receptorNombre);
- }
-
- if (strlen($receptorOtrasSenas) > RECEPTOROTRASSENASMAXSIZE) {
- error_log("RECEPTOROTRASSENASEXTRANJEROMAXSIZE: " . RECEPTOROTRASSENASMAXSIZE . " is greater than receptorOtrasSenas: " . $receptorOtrasSenas);
- }
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- if (count($otrosCargos) > 15) {
- error_log("otrosCargos: " . count($otrosCargos) . " is greater than 15");
- //Delimita el array a solo 15 elementos
- $otrosCargos = array_slice($otrosCargos, 0, 15);
- }
- }
-
- if (isset($mediosPago) && $mediosPago != "") {
- if (count($mediosPago) > 4) {
- error_log("mediosPago: " . count($mediosPago) . " is greater than 4");
- //Delimita el array a solo 4 elementos
- $mediosPago = array_slice($mediosPago, 0, 4);
- }
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $proveedorSistemas . '
- ' . $codigoActividadEmisor . '
- ' . $codigoActividadReceptor . '
- ' . $consecutivo . '
- ' . $fechaEmision . '
-
- ' . $emisorNombre . '
-
- ' . $emisorTipoIdentif . '
- ' . $emisorNumIdentif . '
- ';
-
- if (isset($registroFiscal8707) && $registroFiscal8707 != "") {
- $xmlString .= '
- ' . $registroFiscal8707 . '';
- }
-
- if (isset($emisorNombreComercial) && $emisorNombreComercial != "") {
- $xmlString .= '
- ' . $emisorNombreComercial . '';
- }
-
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $emisorProv . '
- ' . $emisorCanton . '
- ' . $emisorDistrito . '';
- if ($emisorBarrio != '') {
- $xmlString .= '' . $emisorBarrio . '';
- }
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
- }
-
- if ($emisorOtrasSenasExtranjero != '' && strlen($emisorOtrasSenasExtranjero) <= RECEPTOROTRASSENASEXTRANJEROMAXSIZE) {
- $xmlString .= '
- ' . $emisorOtrasSenasExtranjero . '';
- }
-
- if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
- $xmlString .= '
-
- ' . $emisorCodPaisTel . '
- ' . $emisorTel . '
- ';
- }
-
- if ($emisorEmail != '' && preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
- } else {
- error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
- }
-
-
- $xmlString .= '
- ' . $receptorNombre . '';
-
- $xmlString .= '
-
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
-
- if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
- $xmlString .= '
- '
- . $receptorIdentifExtranjero .
- '';
- }
-
- if (isset($receptorNombreComercial) && $receptorNombreComercial != "") {
- $xmlString .= '
- ' . $receptorNombreComercial . '';
- }
-
- if ($receptorProvincia != '' && $receptorCanton != '' && $receptorDistrito != '' && $receptorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $receptorProvincia . '
- ' . $receptorCanton . '
- ' . $receptorDistrito . '';
- if ($receptorBarrio != '') {
- $xmlString .= '' . $receptorBarrio . '';
- }
- $xmlString .= '
- ' . $receptorOtrasSenas . '
- ';
- }
-
- if ($receptorCodPaisTel != '' && $receptorTel != '') {
- $xmlString .= '
-
- ' . $receptorCodPaisTel . '
- ' . $receptorTel . '
- ';
- }
-
- if ($receptorEmail != '') {
- $xmlString .= '' . $receptorEmail . '';
- }
-
- $xmlString .= '';
-
- $xmlString .= '
- ' . $condVenta . '';
-
- if (isset($condVentaOtros) && $condVentaOtros != "") {
- $xmlString .= '
- ' . $condVentaOtros . '';
- }
-
- if (isset($plazoCredito) && $plazoCredito != "") {
- $xmlString .= '
- ' . $plazoCredito . '';
- }
-
- $xmlString .= '
- ';
-
- // cant - unidad medida - detalle - precio unitario - monto total - subtotal - monto total linea - Monto desc -Naturaleza Desc - Impuesto : Codigo / Tarifa / Monto
- /* EJEMPLO DE DETALLES
- {
- "1":["1","Sp","Honorarios","100000","100000","100000","100000","1000","Pronto pago",{"Imp": [{"cod": 122,"tarifa": 1,"monto": 100},{"cod": 133,"tarifa": 1,"monto": 1300}]}],
- "2":["1","Sp","Honorarios","100000","100000","100000","100000"]
- }
- */
- $l = 1;
- foreach ($detalles as $d) {
- $xmlString .= '
-
- ' . $l . '';
-
- $xmlString .= '
- ' . $d->codigoCABYS . '';
-
- if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
- // Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
-
- // Delimitar el array a solo 5 elementos
- if (count($codigoComercialArray) > 5) {
- error_log("codigoComercial: " . count($codigoComercialArray) . " is greater than 5");
- }
- $codigoComercialArray = array_slice($codigoComercialArray, 0, 5);
-
- // Iterar sobre los elementos del array
- foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
- // Verificar si el elemento es un array asociativo
- if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
- $xmlString .= '
-
- ' . $c['tipo'] . '
- ' . $c['codigo'] . '
- ';
- }
- }
- }
-
- $xmlString .= '
- ' . $d->cantidad . '
- ' . $d->unidadMedida . '';
- if (isset($d->tipoTransaccion) && $d->tipoTransaccion != "") {
- $xmlString .= '
- ' . $d->tipoTransaccion . '';
- }
- if (isset($d->unidadMedidaComercial) && $d->unidadMedidaComercial != "") {
- $xmlString .= '
- ' . $d->unidadMedidaComercial . '';
- }
- $xmlString .= '
- ' . $d->detalle . '';
- if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
- $xmlString .= '' . $d->numeroVINoSerie . '';
- }
-
- if (isset($d->registroMedicamento) && $d->registroMedicamento !== "") {
- $xmlString .= '' . htmlspecialchars($d->registroMedicamento) . '';
- }
- if (isset($d->formaFarmaceutica) && $d->formaFarmaceutica !== "") {
- $xmlString .= '' . htmlspecialchars($d->formaFarmaceutica) . '';
- }
-
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
- if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
-
- if (count($descuentoArray) > 5) {
- error_log("descuento: " . count($descuentoArray) . " is greater than 5");
- }
- $descuentoArray = array_slice($descuentoArray, 0, 5);
-
- foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
- if (
- is_array($c) &&
- isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
- isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
- ) {
- $xmlString .= '
-
- ' . $c['montoDescuento'] . '
- ' . $c['codigoDescuento'] . '';
- // CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
- if (
- isset($c['codigoDescuento']) && $c['codigoDescuento'] === "99" &&
- isset($c['codigoDescuentoOTRO']) &&
- strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
- ) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
- }
- // NaturalezaDescuento: minOccurs=0, longitud 3-80
- if (
- isset($c['naturalezaDescuento']) &&
- strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
- ) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
- }
- $xmlString .= '
- ';
- }
- }
- }
-
- $xmlString .= '' . $d->subTotal . '';
-
- if (isset($d->baseImponible) && $d->baseImponible != "") {
- $xmlString .= '' . $d->baseImponible . '';
- }
-
- if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
- $xmlString .= '
-
- ' . $i->codigo . '';
-
- // Add if required
- if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
- ) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
- }
-
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '' . $i->codigoTarifa . '';
- }
-
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '' . $i->tarifa . '';
- }
-
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '' . $i->factorIVA . '';
- }
-
- $xmlString .= '' . $i->monto . '';
-
- if (isset($i->exoneracion) && $i->exoneracion != "") {
- $xmlString .= '
-
- ' . $i->exoneracion->tipoDocumento . '';
- if (isset($i->exoneracion->tipoDocumentoOtro) && !empty($i->exoneracion->tipoDocumentoOtro)) {
- $xmlString .= '' . $i->exoneracion->tipoDocumentoOtro . '';
- }
- $xmlString .= '' . $i->exoneracion->numeroDocumento . '';
- if (isset($i->exoneracion->numeroArticulo) && !empty($i->exoneracion->numeroArticulo)) {
- $xmlString .= '' . $i->exoneracion->numeroArticulo . '';
- }
- if (isset($i->exoneracion->numeroInciso) && !empty($i->exoneracion->numeroInciso)) {
- $xmlString .= '' . $i->exoneracion->numeroInciso . '';
- }
- $xmlString .= '' . $i->exoneracion->nombreInstitucion . '';
- if (isset($i->exoneracion->nombreInstitucionOtros) && !empty($i->exoneracion->nombreInstitucionOtros)) {
- $xmlString .= '' . $i->exoneracion->nombreInstitucionOtros . '';
- }
- $xmlString .= '
- ' . $i->exoneracion->fechaEmision . '
- ' . $i->exoneracion->tarifaExoneracion . '
- ' . $i->exoneracion->montoExoneracion . '
- ';
- }
-
- $xmlString .= '';
- }
- }
-
- $xmlString .= '' . $d->impuestoNeto . '';
- $xmlString .= '' . $d->montoTotalLinea . '';
- $xmlString .= '';
- $l++;
- }
-
- $xmlString .= '';
- //OtrosCargos
- if (isset($otrosCargos) && $otrosCargos != "") {
- foreach ($otrosCargos as $o) {
- $xmlString .= '
-
- ' . $o->tipoDocumentoOC . '';
- if (isset($o->tipoDocumentoOTROS) && $o->tipoDocumentoOTROS != "") {
- $xmlString .= '
- ' . $o->tipoDocumentoOTROS . '';
- }
- if (isset($o->numeroIdentidadTercero) && $o->numeroIdentidadTercero != "" && isset($o->tipoIdentidadTercero) && $o->tipoIdentidadTercero != "") {
- $xmlString .= '
-
- ' . $o->tipoIdentidadTercero . '
- ' . $o->numeroIdentidadTercero . '
- ';
- }
- if (isset($o->nombreTercero) && $o->nombreTercero != "") {
- $xmlString .= '
- ' . $o->nombreTercero . '';
- }
- $xmlString .= '
- ' . $o->detalle . '';
- if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
- $xmlString .= '
- ' . $o->porcentajeOC . '';
- }
- $xmlString .= '
- ' . $o->montoCargo . '';
- $xmlString .= '
- ';
- }
- }
-
- $xmlString .= '
- ';
-
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
- ' . $codMoneda . '
- ' . $tipoCambio . '
- ';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalServExonerados != '') {
- $xmlString .= '
- ' . $totalServExonerados . '';
- }
-
- if ($totalServNoSujeto != '') {
- $xmlString .= '
- ' . $totalServNoSujeto . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalMercExonerada != '') {
- $xmlString .= '
- ' . $totalMercExonerada . '';
- }
-
- if ($totalMercNoSujeta != '') {
- $xmlString .= '
- ' . $totalMercNoSujeta . '';
- }
-
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
-
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
-
- if ($totalExonerado != '') {
- $xmlString .= '
- ' . $totalExonerado . '';
- }
-
- if ($totalNoSujeto != '') {
- $xmlString .= '
- ' . $totalNoSujeto . '';
- }
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
- }
-
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
- }
- $xmlString .= '';
- }
- }
-
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
- }
-
- if ($totalImpAsumidoEmisorFabrica != '') {
- $xmlString .= '
- ' . $totalImpAsumidoEmisorFabrica . '';
- }
-
- if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
- $xmlString .= '
- ' . $totalOtrosCargos . '';
- }
-
- if (isset($mediosPago) && !empty($mediosPago)) {
- foreach ($mediosPago as $o) {
- $xmlString .= '
- ';
-
- // Add TipoMedioPago
- if (isset($o->tipoMedioPago) && !empty($o->tipoMedioPago)) {
- $xmlString .= '' . $o->tipoMedioPago . '';
- }
-
- // Add MedioPagoOtros (only if TipoMedioPago is "99")
- if (isset($o->tipoMedioPago) && $o->tipoMedioPago === "99" && isset($o->medioPagoOtros) && !empty($o->medioPagoOtros)) {
- $xmlString .= '' . htmlspecialchars($o->medioPagoOtros) . '';
- }
-
- // Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
- }
-
- $xmlString .= '';
- }
- }
-
- $xmlString .= '
- ' . $totalComprobante . '
- ';
-
- if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
- foreach ($informacionReferencia as $ref) {
- if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
- if (in_array($ref->tipoDoc, TIPODOCREFVALUES, true)) {
- $xmlString .= '';
- $xmlString .= '' . $ref->tipoDoc . '';
- if ($ref->tipoDoc === '99' && isset($ref->tipoDocOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->tipoDocOtro) . '';
- }
- if (isset($ref->numero)) {
- $xmlString .= '' . $ref->numero . '';
- }
- $xmlString .= '' . $ref->fechaEmision . '';
- if (isset($ref->codigo)) {
- $xmlString .= '' . $ref->codigo . '';
- if ($ref->codigo === '99' && isset($ref->codigoOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->codigoOtro) . '';
- }
- }
- if (isset($ref->razon)) {
- $xmlString .= '' . $ref->razon . '';
- }
- $xmlString .= '';
- } else {
- grace_error("El parámetro tipoDoc no cumple con la estructura establecida. tipoDoc = " . $ref->tipoDoc);
- }
- }
- }
- }
-
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
- // Start Otros element
- $xmlString .= '';
-
- // Handle multiple OtroTexto elements
- if (isset($otros->otroTexto)) {
- if (is_array($otros->otroTexto)) {
- foreach ($otros->otroTexto as $otroTexto) {
- $codigo = isset($otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otroTexto->codigo) . '"' : '';
- $texto = isset($otroTexto->texto) ? htmlspecialchars($otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- } else {
- $codigo = isset($otros->otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otros->otroTexto->codigo) . '"' : '';
- $texto = isset($otros->otroTexto->texto) ? htmlspecialchars($otros->otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- }
-
- // Handle multiple OtroContenido elements
- if (isset($otros->otroContenido) && is_array($otros->otroContenido)) {
- foreach ($otros->otroContenido as $otroContenido) {
- $codigo = isset($otroContenido->codigo) ? ' codigo="' . htmlspecialchars($otroContenido->codigo) . '"' : '';
- $contenido = '';
- if (isset($otroContenido->contenidoEstructurado) && is_object($otroContenido->contenidoEstructurado)) {
- foreach ($otroContenido->contenidoEstructurado as $tag => $data) {
- $contenido .= '<' . $tag . '>';
- if (is_object($data)) {
- foreach ($data as $k => $v) {
- $contenido .= '<' . $k . '>' . htmlspecialchars($v) . '' . $k . '>';
- }
- }
- $contenido .= '' . $tag . '>';
- }
- }
- $xmlString .= '' . $contenido . '';
- }
- }
-
- $xmlString .= '';
-
- // XML Resultante
- //
- // Texto opcional 1
- //
- //
- // developer@example.com
- // Developer Name
- // +123456789
- //
- //
- //
- //
- // support@example.com
- // Support Team
- // +987654321
- //
- //
- //
-
- $xmlString .= '
- ';
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-function genXMLFee()
-{
- $clave = params_get("clave");
- $proveedorSistemas = params_get("proveedor_sistemas");
- $codigoActividadEmisor = params_get("codigo_actividad_emisor"); // https://cloud-cube.s3.amazonaws.com/sp5z9nxkd1ra/public/assets/json/actividades_por_codigo.json
- $consecutivo = params_get("consecutivo");
- $fechaEmision = params_get("fecha_emision");
-
- $emisorNombre = params_get("emisor_nombre");
- $emisorTipoIdentif = params_get("emisor_tipo_identif");
- $emisorNumIdentif = params_get("emisor_num_identif");
- $emisorNombreComercial = params_get("emisor_nombre_comercial");
- $emisorProv = params_get("emisor_provincia");
- $emisorCanton = params_get("emisor_canton");
- $emisorDistrito = params_get("emisor_distrito");
- $emisorBarrio = params_get("emisor_barrio");
- $emisorOtrasSenas = params_get("emisor_otras_senas");
- $emisorCodPaisTel = params_get("emisor_cod_pais_tel");
- $emisorTel = params_get("emisor_tel");
- $emisorEmail = params_get("emisor_email");
- $registroFiscal8707 = params_get("registrofiscal8707");
-
- $receptorNombre = params_get("receptor_nombre");
- $receptorTipoIdentif = params_get("receptor_tipo_identif");
- $receptorNumIdentif = params_get("receptor_num_identif");
- $receptorIdentifExtranjero = params_get("receptor_identif_extranjero");
- $receptorNombreComercial = params_get("receptor_nombre_comercial");
- $receptorOtrasSenasExtranjero = params_get("receptor_otras_senas_extranjero");
- $receptorCodPaisTel = params_get("receptor_cod_pais_tel");
- $receptorTel = params_get("receptor_tel");
- $receptorEmail = params_get("receptor_email");
-
- $condVenta = params_get("condicion_venta");
- $condVentaOtros = params_get("condicion_venta_otros");
- $plazoCredito = params_get("plazo_credito");
- $detalles = json_decode(params_get("detalles"));
- $otrosCargos = json_decode(params_get("otrosCargos"));
- $codMoneda = params_get("cod_moneda");
- $tipoCambio = params_get("tipo_cambio");
-
- $totalServGravados = params_get("total_serv_gravados");
- $totalServExentos = params_get("total_serv_exentos");
- $totalMercGravadas = params_get("total_merc_gravada");
- $totalMercExentas = params_get("total_merc_exenta");
- $totalGravados = params_get("total_gravados");
- $totalExento = params_get("total_exento");
- $totalVentas = params_get("total_ventas");
- $totalDescuentos = params_get("total_descuentos");
- $totalVentasNeta = params_get("total_ventas_neta");
- $totalImp = params_get("total_impuestos");
- $totalImpAsumidoEmisorFabrica = params_get("total_impuestos_asumidos_fabrica");
- $totalOtrosCargos = params_get("totalOtrosCargos");
- $totalComprobante = params_get("total_comprobante");
-
- $informacionReferencia = json_decode(params_get("informacion_referencia"));
- $otros = json_decode(params_get('otros'));
- // Resumen
- $totalDesgloseImpuesto = json_decode(params_get("totalDesgloseImpuesto"));
-
- grace_debug(params_get("detalles"));
-
- if (isset($otrosCargos) && $otrosCargos != "") {
- grace_debug(params_get("otrosCargos"));
- }
-
- if (isset($totalDesgloseImpuesto) && $totalDesgloseImpuesto != "") {
- grace_debug(params_get("totalDesgloseImpuesto"));
- }
-
- // Validate string sizes
- $codigoActividadEmisor = str_pad($codigoActividadEmisor, 6, "0", STR_PAD_LEFT);
- if (strlen($codigoActividadEmisor) != CODIGOACTIVIDADSIZE) {
- error_log("codigoActividadSize is: " . CODIGOACTIVIDADSIZE . " and codigoActividadEmisor is " . $codigoActividadEmisor);
- }
-
- if (strlen($emisorNombre) > EMISORNOMBREMAXSIZE) {
- error_log("emisorNombreSize: " . EMISORNOMBREMAXSIZE . " is greater than emisorNombre: " . $emisorNombre);
- }
-
- if (strlen($receptorNombre) > RECEPTORNOMBREMAXSIZE) {
- error_log("receptorNombreMaxSize: " . RECEPTORNOMBREMAXSIZE . " is greater than receptorNombre: " . $receptorNombre);
- }
-
- if (strlen($receptorOtrasSenasExtranjero) > RECEPTOROTRASSENASMAXSIZE) {
- error_log("RECEPTOROTRASSENASEXTRANJEROMAXSIZE: " . RECEPTOROTRASSENASMAXSIZE . " is greater than receptorOtrasSenas: " . $receptorOtrasSenasExtranjero);
- }
-
- if (isset($otrosCargos) && !empty($otrosCargos)) {
- if (count($otrosCargos->otrosCargos) > 15) {
- error_log("otrosCargos: " . count($otrosCargos->otrosCargos) . " is greater than 15");
- //Delimita el array a solo 4 elementos
- $otrosCargos->otrosCargos = array_slice($otrosCargos->otrosCargos, 0, 15);
- }
- }
-
- $xmlString = '
-
- ' . $clave . '
- ' . $proveedorSistemas . '
- ' . $codigoActividadEmisor . '
- ' . $consecutivo . '
- ' . $fechaEmision . '
-
- ' . $emisorNombre . '
-
- ' . $emisorTipoIdentif . '
- ' . $emisorNumIdentif . '
- ';
-
- if (isset($registroFiscal8707) && $registroFiscal8707 != "") {
- $xmlString .= '
- ' . $registroFiscal8707 . '';
- }
-
- if (isset($emisorNombreComercial) && $emisorNombreComercial != "") {
- $xmlString .= '
- ' . $emisorNombreComercial . '';
- }
-
- if ($emisorProv != '' && $emisorCanton != '' && $emisorDistrito != '' && $emisorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $emisorProv . '
- ' . $emisorCanton . '
- ' . $emisorDistrito . '';
- if ($emisorBarrio != '') {
- $xmlString .= '' . $emisorBarrio . '';
- }
- $xmlString .= '
- ' . $emisorOtrasSenas . '
- ';
- }
-
- if ($emisorCodPaisTel != '' && $emisorTel != '' && $emisorTel >= EMISORNUMEROTELMIN && $emisorTel <= EMISORNUMEROTELMAX) {
- $xmlString .= '
-
- ' . $emisorCodPaisTel . '
- ' . $emisorTel . '
- ';
- }
-
- if (preg_match(EMAIL_REGEX, trim($emisorEmail))) {
- $xmlString .= '' . trim($emisorEmail) . '';
- } else {
- error_log(sprintf("Invalid email format: '%s' does not meet the regex pattern: %s", $emisorEmail, EMAIL_REGEX));
- }
-
- if (isset($receptorNombre) && $receptorNombre != "") {
- $xmlString .= '
- ' . $receptorNombre . '';
- }
-
- if (isset($receptorTipoIdentif) && $receptorTipoIdentif != "" && isset($receptorNumIdentif) && $receptorNumIdentif != "") {
- $xmlString .= '
-
- ' . $receptorTipoIdentif . '
- ' . $receptorNumIdentif . '
- ';
- }
-
- if ($receptorIdentifExtranjero != '' && $receptorIdentifExtranjero != '') {
- $xmlString .= '
- '
- . $receptorIdentifExtranjero .
- '';
- }
-
- if (isset($receptorNombreComercial) && $receptorNombreComercial != "") {
- $xmlString .= '
- ' . $receptorNombreComercial . '';
- }
-
- if (isset($receptorProvincia) && $receptorProvincia != '' && $receptorCanton != '' && $receptorDistrito != '' && $receptorOtrasSenas != '') {
- $xmlString .= '
-
- ' . $receptorProvincia . '
- ' . $receptorCanton . '
- ' . $receptorDistrito . '';
- if ($receptorBarrio != '') {
- $xmlString .= '' . $receptorBarrio . '';
- }
- $xmlString .= '
- ' . $receptorOtrasSenas . '
- ';
- }
-
- if ($receptorOtrasSenasExtranjero != '' && strlen($receptorOtrasSenasExtranjero) <= RECEPTOROTRASSENASEXTRANJEROMAXSIZE) {
- $xmlString .= '
- '
- . $receptorOtrasSenasExtranjero .
- '';
- }
-
-
- if ($receptorCodPaisTel != '' && $receptorTel != '') {
- $xmlString .= '
-
- ' . $receptorCodPaisTel . '
- ' . $receptorTel . '
- ';
- }
-
- if ($receptorEmail != '') {
- $xmlString .= '' . $receptorEmail . '';
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $condVenta . '';
-
- if (isset($condVentaOtros) && $condVentaOtros != "") {
- $xmlString .= '
- ' . $condVentaOtros . '';
- }
-
- if (isset($plazoCredito) && $plazoCredito != "") {
- $xmlString .= '
- ' . $plazoCredito . '';
- }
-
- $xmlString .= '
- ';
-
-
- $l = 1;
- foreach ($detalles as $d) {
- $xmlString .= '
-
- ' . $l . '';
-
- if (isset($d->partidaArancelaria) && $d->partidaArancelaria != "") {
- $xmlString .= '
- ' . $d->partidaArancelaria . '';
- }
-
- $xmlString .= '
- ' . $d->codigoCABYS . '';
-
- if (isset($d->codigoComercial) && !empty($d->codigoComercial)) {
- // Convertir el objeto $d->codigoComercial en un array
- $codigoComercialArray = (array)$d->codigoComercial;
-
- // Delimitar el array a solo 5 elementos
- if (count($codigoComercialArray) > 5) {
- error_log("codigoComercial: " . count($codigoComercialArray) . " is greater than 5");
- }
- $codigoComercialArray = array_slice($codigoComercialArray, 0, 5);
-
- // Iterar sobre los elementos del array
- foreach ($codigoComercialArray as $codigos) {
- $c = (array)$codigos;
- // Verificar si el elemento es un array asociativo
- if (is_array($c) && isset($c['tipo']) && $c['tipo'] != "" && isset($c['codigo']) && $c['codigo'] != "") {
- $xmlString .= '
-
- ' . $c['tipo'] . '
- ' . $c['codigo'] . '
- ';
- }
- }
- }
-
-
- $xmlString .= '
- ' . $d->cantidad . '
- ' . $d->unidadMedida . '';
- if (isset($d->tipoTransaccion) && $d->tipoTransaccion != "") {
- $xmlString .= '
- ' . $d->tipoTransaccion . '';
- }
- if (isset($d->unidadMedidaComercial) && $d->unidadMedidaComercial != "") {
- $xmlString .= '
- ' . $d->unidadMedidaComercial . '';
- }
- $xmlString .= '
- ' . $d->detalle . '';
- if (isset($d->numeroVINoSerie) && $d->numeroVINoSerie != "") {
- $xmlString .= '' . $d->numeroVINoSerie . '';
- }
-
- if (isset($d->registroMedicamento) && $d->registroMedicamento !== "") {
- $xmlString .= '' . htmlspecialchars($d->registroMedicamento) . '';
- }
- if (isset($d->formaFarmaceutica) && $d->formaFarmaceutica !== "") {
- $xmlString .= '' . htmlspecialchars($d->formaFarmaceutica) . '';
- }
-
- if (isset($d->detalleSurtido) && is_array($d->detalleSurtido) && count($d->detalleSurtido) > 0) {
- $xmlString .= '';
- $lineas = array_slice($d->detalleSurtido, 0, 20);
- foreach ($lineas as $linea) {
- $xmlString .= '';
- $xmlString .= '' . $linea->codigoCABYSSurtido . '';
- if (isset($linea->codigoComercialSurtido) && is_array($linea->codigoComercialSurtido)) {
- $codigos = array_slice($linea->codigoComercialSurtido, 0, 5);
- foreach ($codigos as $codigo) {
- $xmlString .= '';
- $xmlString .= '' . $codigo->tipoSurtido . '';
- $xmlString .= '' . $codigo->codigoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->cantidadSurtido . '';
- $xmlString .= '' . $linea->unidadMedidaSurtido . '';
- if (isset($linea->unidadMedidaComercialSurtido)) {
- $xmlString .= '' . $linea->unidadMedidaComercialSurtido . '';
- }
- $xmlString .= '' . $linea->detalleSurtido . '';
- $xmlString .= '' . $linea->precioUnitarioSurtido . '';
- $xmlString .= '' . $linea->montoTotalSurtido . '';
- if (isset($linea->descuentoSurtido) && is_array($linea->descuentoSurtido)) {
- $descuentos = array_slice($linea->descuentoSurtido, 0, 5);
- foreach ($descuentos as $desc) {
- $xmlString .= '';
- $xmlString .= '' . $desc->montoDescuentoSurtido . '';
- $xmlString .= '' . $desc->codigoDescuentoSurtido . '';
- if (isset($desc->descuentoSurtidoOtros)) {
- $xmlString .= '' . $desc->descuentoSurtidoOtros . '';
- }
- $xmlString .= '';
- }
- }
- $xmlString .= '' . $linea->subTotalSurtido . '';
- if (isset($linea->ivaCobradoFabricaSurtido)) {
- $xmlString .= '' . $linea->ivaCobradoFabricaSurtido . '';
- }
- $xmlString .= '' . $linea->baseImponibleSurtido . '';
- if (isset($linea->impuestoSurtido) && is_array($linea->impuestoSurtido)) {
- $impuestos = array_slice($linea->impuestoSurtido, 0, 1000);
- foreach ($impuestos as $imp) {
- $xmlString .= '';
- $xmlString .= '' . $imp->codigoImpuestoSurtido . '';
- if (isset($imp->codigoImpuestoOTROSurtido)) {
- $xmlString .= '' . $imp->codigoImpuestoOTROSurtido . '';
- }
- if (isset($imp->codigoTarifaIVASurtido)) {
- $xmlString .= '' . $imp->codigoTarifaIVASurtido . '';
- }
- if (isset($imp->tarifaSurtido)) {
- $xmlString .= '' . $imp->tarifaSurtido . '';
- }
- if (isset($imp->datosImpuestoEspecificoSurtido)) {
- $e = $imp->datosImpuestoEspecificoSurtido;
- $xmlString .= '';
- if (isset($e->cantidadUnidadMedidaSurtido)) {
- $xmlString .= '' . $e->cantidadUnidadMedidaSurtido . '';
- }
- if (isset($e->porcentajeSurtido)) {
- $xmlString .= '' . $e->porcentajeSurtido . '';
- }
- if (isset($e->proporcionSurtido)) {
- $xmlString .= '' . $e->proporcionSurtido . '';
- }
- if (isset($e->volumenUnidadConsumoSurtido)) {
- $xmlString .= '' . $e->volumenUnidadConsumoSurtido . '';
- }
- if (isset($e->impuestoUnidadSurtido)) {
- $xmlString .= '' . $e->impuestoUnidadSurtido . '';
- }
- $xmlString .= '';
- }
- $xmlString .= '' . $imp->montoImpuestoSurtido . '';
- $xmlString .= '';
- }
- }
- $xmlString .= '';
- }
- $xmlString .= '';
- }
-
- $xmlString .= '
- ' . $d->precioUnitario . '
- ' . $d->montoTotal . '';
-
- if (isset($d->descuento) && !empty($d->descuento)) {
- $descuentoArray = (array)$d->descuento;
-
- if (count($descuentoArray) > 5) {
- error_log("descuento: " . count($descuentoArray) . " is greater than 5");
- }
- $descuentoArray = array_slice($descuentoArray, 0, 5);
-
- foreach ($descuentoArray as $descuentos) {
- $c = (array)$descuentos;
- if (
- is_array($c) &&
- isset($c['montoDescuento']) && $c['montoDescuento'] !== "" &&
- isset($c['codigoDescuento']) && $c['codigoDescuento'] !== ""
- ) {
- $xmlString .= '
-
- ' . $c['montoDescuento'] . '
- ' . $c['codigoDescuento'] . '';
- // CodigoDescuentoOTRO: obligatorio si codigoDescuento == "99" y existe el campo
- if (
- isset($c['codigoDescuento']) && $c['codigoDescuento'] === "99" &&
- isset($c['codigoDescuentoOTRO']) &&
- strlen($c['codigoDescuentoOTRO']) >= 5 && strlen($c['codigoDescuentoOTRO']) <= 100
- ) {
- $xmlString .= '' . htmlspecialchars($c['codigoDescuentoOTRO']) . '';
- }
- // NaturalezaDescuento: minOccurs=0, longitud 3-80
- if (
- isset($c['naturalezaDescuento']) &&
- strlen($c['naturalezaDescuento']) >= 3 && strlen($c['naturalezaDescuento']) <= 80
- ) {
- $xmlString .= '' . htmlspecialchars($c['naturalezaDescuento']) . '';
- }
- $xmlString .= '
- ';
- }
- }
- }
-
- $xmlString .= '' . $d->subTotal . '';
-
- if (isset($d->impuesto) && $d->impuesto != "") {
- foreach ($d->impuesto as $i) {
- $xmlString .= '
- ';
- if (isset($i->codigo) && $i->codigo != "") {
- $xmlString .= '' . $i->codigo . '';
- }
-
- // Add if required
- if (
- isset($i->codigo) && $i->codigo == "99" &&
- isset($i->codigoImpuestoOtro) && !empty($i->codigoImpuestoOtro)
- ) {
- $xmlString .= '' . $i->codigoImpuestoOtro . '';
- }
-
- if (isset($i->codigoTarifa) && $i->codigoTarifa != "") {
- $xmlString .= '' . $i->codigoTarifa . '';
- }
-
- if (isset($i->tarifa) && $i->tarifa != "") {
- $xmlString .= '' . $i->tarifa . '';
- }
-
- if (isset($i->factorIVA) && $i->factorIVA != "") {
- $xmlString .= '' . $i->factorIVA . '';
- }
-
- if (isset($i->monto) && $i->monto != "") {
- $xmlString .= '' . $i->monto . '';
- }
-
- if (isset($i->montoExportacion) && $i->montoExportacion != "") {
- $xmlString .= '' . $i->montoExportacion . '';
- }
-
- $xmlString .= '';
- }
- }
-
- if (isset($d->impuestoNeto) && $d->impuestoNeto != "") {
- $xmlString .= '' . $d->impuestoNeto . '';
- }
- $xmlString .= '' . $d->montoTotalLinea . '';
- $xmlString .= '';
- $l++;
- }
-
- $xmlString .= '';
-
- // JSON DE EJEMPLO
- // [
- // {
- // "tipoDocumentoOC": "10",
- // "tipoDocumentoOTROS": "string",
- // "tipoIdentidadTercero": "01",
- // "numeroIdentidadTercero": "160029688",
- // "nombreTercero": "John Doe",
- // "detalle": "Additional charge for service",
- // "porcentajeOC": "1452590.23",
- // "montoCargo": "1258720.23491"
- // },
- // {
- // "tipoDocumentoOC": "20",
- // "tipoDocumentoOTROS": "other",
- // "tipoIdentidadTercero": "02",
- // "numeroIdentidadTercero": "123456789",
- // "nombreTercero": "Jane Smith",
- // "detalle": "Extra fee for expedited processing",
- // "porcentajeOC": "10.50",
- // "montoCargo": "500.00"
- // }
- // ]
-
- //OtrosCargos
- if (isset($otrosCargos) && $otrosCargos != "") {
- foreach ($otrosCargos as $o) {
- $xmlString .= '
-
- ' . $o->tipoDocumentoOC . '';
- if (isset($o->tipoDocumentoOTROS) && $o->tipoDocumentoOTROS != "") {
- $xmlString .= '
- ' . $o->tipoDocumentoOTROS . '';
- }
- if (isset($o->numeroIdentidadTercero) && $o->numeroIdentidadTercero != "" && isset($o->tipoIdentidadTercero) && $o->tipoIdentidadTercero != "") {
- $xmlString .= '
-
- ' . $o->tipoIdentidadTercero . '
- ' . $o->numeroIdentidadTercero . '
- ';
- }
- if (isset($o->nombreTercero) && $o->nombreTercero != "") {
- $xmlString .= '
- ' . $o->nombreTercero . '';
- }
- $xmlString .= '
- ' . $o->detalle . '';
- if (isset($o->porcentajeOC) && $o->porcentajeOC != "") {
- $xmlString .= '
- ' . $o->porcentajeOC . '';
- }
- $xmlString .= '
- ' . $o->montoCargo . '';
- $xmlString .= '
- ';
- }
- }
-
- // XML Resultante
- //
- // 10
- // string
- //
- // 01
- // 160029688
- //
- // John Doe
- // Additional charge for service
- // 1452590.23
- // 1258720.23491
- //
- //
- // 20
- // other
- //
- // 02
- // 123456789
- //
- // Jane Smith
- // Extra fee for expedited processing
- // 10.50
- // 500.00
- //
-
- $xmlString .= '
- ';
-
- if ($codMoneda != '' && $codMoneda != 'CRC' && $tipoCambio != '' && $tipoCambio != 0) {
- $xmlString .= '
-
- ' . $codMoneda . '
- ' . $tipoCambio . '
- ';
- } else {
- $xmlString .= '
-
- CRC
- 1
- ';
- }
-
- if ($totalServGravados != '') {
- $xmlString .= '
- ' . $totalServGravados . '';
- }
-
- if ($totalServExentos != '') {
- $xmlString .= '
- ' . $totalServExentos . '';
- }
-
- if ($totalMercGravadas != '') {
- $xmlString .= '
- ' . $totalMercGravadas . '';
- }
-
- if ($totalMercExentas != '') {
- $xmlString .= '
- ' . $totalMercExentas . '';
- }
-
- if ($totalGravados != '') {
- $xmlString .= '
- ' . $totalGravados . '';
- }
-
- if ($totalExento != '') {
- $xmlString .= '
- ' . $totalExento . '';
- }
-
- $xmlString .= '
- ' . $totalVentas . '';
-
- if ($totalDescuentos != '') {
- $xmlString .= '
- ' . $totalDescuentos . '';
- }
-
- $xmlString .= '
- ' . $totalVentasNeta . '';
-
- // Add logic for TotalDesgloseImpuesto
- if (isset($totalDesgloseImpuesto) && !empty($totalDesgloseImpuesto)) {
- foreach ($totalDesgloseImpuesto as $impuesto) {
- $xmlString .= '
- ';
- if (isset($impuesto->Codigo)) {
- $xmlString .= '' . $impuesto->Codigo . '';
- }
- if (isset($impuesto->CodigoTarifaIVA)) {
- $xmlString .= '' . $impuesto->CodigoTarifaIVA . '';
- }
- if (isset($impuesto->TotalMontoImpuesto)) {
- $xmlString .= '' . $impuesto->TotalMontoImpuesto . '';
- }
- $xmlString .= '';
- }
- }
-
- if ($totalImp != '') {
- $xmlString .= '
- ' . $totalImp . '';
- }
-
- if ($totalImpAsumidoEmisorFabrica != '') {
- $xmlString .= '
- ' . $totalImpAsumidoEmisorFabrica . '';
- }
-
- if (isset($totalOtrosCargos) && $totalOtrosCargos != "") {
- $xmlString .= '
- ' . $totalOtrosCargos . '';
- }
-
- if (isset($mediosPago) && !empty($mediosPago)) {
- foreach ($mediosPago as $o) {
- $xmlString .= '
- ';
-
- // Add TipoMedioPago
- if (isset($o->tipoMedioPago) && !empty($o->tipoMedioPago)) {
- $xmlString .= '' . $o->tipoMedioPago . '';
- }
-
- // Add MedioPagoOtros (only if TipoMedioPago is "99")
- if (isset($o->tipoMedioPago) && $o->tipoMedioPago === "99" && isset($o->medioPagoOtros) && !empty($o->medioPagoOtros)) {
- $xmlString .= '' . htmlspecialchars($o->medioPagoOtros) . '';
- }
-
- // Add TotalMedioPago
- if (isset($o->totalMedioPago) && is_numeric($o->totalMedioPago)) {
- $xmlString .= '' . number_format($o->totalMedioPago, 2, '.', '') . '';
- }
-
- $xmlString .= '';
- }
- }
-
-
- $xmlString .= '
- ' . $totalComprobante . '
- ';
-
- // JSON de ejemplo
- // {
- // "informacionReferencia": [
- // {
- // "tipoDoc": "01",
- // "tipoDocOtro": "Factura",
- // "numero": "50620032400020536006000100001010000000017100000017",
- // "fechaEmision": "2023-10-01T12:00:00",
- // "codigo": "99",
- // "codigoOtro": "OTRO1",
- // "razon": "Corrección de datos"
- // },
- // {
- // "tipoDoc": "02",
- // "numero": "50620032400020536006000100001010000000017200000018",
- // "fechaEmision": "2023-10-02T15:30:00",
- // "codigo": "01",
- // "razon": "Devolución de producto"
- // }
- // ]
- // }
-
- if (is_array($informacionReferencia) && count($informacionReferencia) > 0) {
- foreach ($informacionReferencia as $ref) {
- if (!empty($ref->tipoDoc) && !empty($ref->fechaEmision)) {
- if (in_array($ref->tipoDoc, TIPODOCREFVALUES, true)) {
- $xmlString .= '';
- $xmlString .= '' . $ref->tipoDoc . '';
- if ($ref->tipoDoc === '99' && isset($ref->tipoDocOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->tipoDocOtro) . '';
- }
- if (isset($ref->numero)) {
- $xmlString .= '' . $ref->numero . '';
- }
- $xmlString .= '' . $ref->fechaEmision . '';
- if (isset($ref->codigo)) {
- $xmlString .= '' . $ref->codigo . '';
- if ($ref->codigo === '99' && isset($ref->codigoOtro)) {
- $xmlString .= '' . htmlspecialchars($ref->codigoOtro) . '';
- }
- }
- if (isset($ref->razon)) {
- $xmlString .= '' . $ref->razon . '';
- }
- $xmlString .= '';
- } else {
- grace_error("El parámetro tipoDoc no cumple con la estructura establecida. tipoDoc = " . $ref->tipoDoc);
- }
- }
- }
- }
-
- // XML Resultante
- //
- // 01
- // Factura
- // 50620032400020536006000100001010000000017100000017
- // 2023-10-01T12:00:00
- // 99
- // OTRO1
- // Corrección de datos
- //
- //
- // 02
- // 50620032400020536006000100001010000000017200000018
- // 2023-10-02T15:30:00
- // 01
- // Devolución de producto
- //
-
- // -----------------------------------------------------------------------------------------------------
-
- // JSON de ejemplo
- // {
- // "otroTexto": {
- // "codigo": "COD1",
- // "texto": "Texto opcional 1"
- // },
- // "otroContenido": [
- // {
- // "codigo": "CONT1",
- // "contenidoEstructurado": {
- // "ContactoDesarrollador": {
- // "Correo": "developer@example.com",
- // "Nombre": "Developer Name",
- // "Telefono": "+123456789"
- // }
- // }
- // },
- // {
- // "codigo": "CONT2",
- // "contenidoEstructurado": {
- // "SoporteTecnico": {
- // "Correo": "support@example.com",
- // "Nombre": "Support Team",
- // "Telefono": "+987654321"
- // }
- // }
- // }
- // ]
- //}
-
- // Start Otros element
- $xmlString .= '';
-
- // Handle multiple OtroTexto elements
- if (isset($otros->otroTexto)) {
- if (is_array($otros->otroTexto)) {
- foreach ($otros->otroTexto as $otroTexto) {
- $codigo = isset($otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otroTexto->codigo) . '"' : '';
- $texto = isset($otroTexto->texto) ? htmlspecialchars($otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- } else {
- $codigo = isset($otros->otroTexto->codigo) ? ' codigo="' . htmlspecialchars($otros->otroTexto->codigo) . '"' : '';
- $texto = isset($otros->otroTexto->texto) ? htmlspecialchars($otros->otroTexto->texto) : '';
- $xmlString .= '' . $texto . '';
- }
- }
-
- // Handle multiple OtroContenido elements
- if (isset($otros->otroContenido) && is_array($otros->otroContenido)) {
- foreach ($otros->otroContenido as $otroContenido) {
- $codigo = isset($otroContenido->codigo) ? ' codigo="' . htmlspecialchars($otroContenido->codigo) . '"' : '';
- $contenido = '';
- if (isset($otroContenido->contenidoEstructurado) && is_object($otroContenido->contenidoEstructurado)) {
- foreach ($otroContenido->contenidoEstructurado as $tag => $data) {
- $contenido .= '<' . $tag . '>';
- if (is_object($data)) {
- foreach ($data as $k => $v) {
- $contenido .= '<' . $k . '>' . htmlspecialchars($v) . '' . $k . '>';
- }
- }
- $contenido .= '' . $tag . '>';
- }
- }
- $xmlString .= '' . $contenido . '';
- }
- }
-
- $xmlString .= '';
-
- // XML Resultante
- //
- // Texto opcional 1
- //
- //
- // developer@example.com
- // Developer Name
- // +123456789
- //
- //
- //
- //
- // support@example.com
- // Support Team
- // +987654321
- //
- //
- //
-
- $xmlString .= '
- ';
- $arrayResp = array(
- "clave" => $clave,
- "xml" => base64_encode($xmlString)
- );
-
- return $arrayResp;
-}
-
-
-/* * ************************************************** */
-/* Funcion de prueba */
-/* * ************************************************** */
-
-function test()
-{
- return "Esto es un test";
-}
-
-?>
diff --git a/api/contrib/genXML/module.php b/api/contrib/genXML/module.php
index bcf071aa..57fd1b52 100644
--- a/api/contrib/genXML/module.php
+++ b/api/contrib/genXML/module.php
@@ -118,7 +118,7 @@ function genXML_init()
array("key" => "total_comprobante", "def" => "", "req" => true),
array("key" => "otros", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -193,7 +193,7 @@ function genXML_init()
array("key" => "otros", "def" => "", "req" => false),
array("key" => "otrosType", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -268,7 +268,7 @@ function genXML_init()
array("key" => "otros", "def" => "", "req" => false),
array("key" => "otrosType", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -342,7 +342,7 @@ function genXML_init()
array("key" => "otros", "def" => "", "req" => false),
array("key" => "otrosType", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -415,7 +415,7 @@ function genXML_init()
array("key" => "otros", "def" => "", "req" => false),
array("key" => "otrosType", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -475,7 +475,7 @@ function genXML_init()
array("key" => "total_impuestos_asumidos_fabrica", "def" => "", "req" => false),
array("key" => "totalOtrosCargos", "def" => "", "req" => false),
array("key" => "total_comprobante", "def" => "", "req" => true),
- array("key" => "informacion_referencia", "def" => "", "req" => true),
+ array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otros", "def" => "", "req" => false)
),
'file' => 'genXML.php'
@@ -520,3 +520,4 @@ function MODULENAME_access()
/**@}*/
/** @}*/
+
diff --git a/api/contrib/signXML/Firmadohaciendacr.php b/api/contrib/signXML/Firmadohaciendacr.php
index 98a7c04d..8e0a2f03 100644
--- a/api/contrib/signXML/Firmadohaciendacr.php
+++ b/api/contrib/signXML/Firmadohaciendacr.php
@@ -95,7 +95,7 @@ public function firmar($certificadop12, $clavecertificado, $xmlsinfirma, $tipodo
$this->Modulus = base64_encode($complem['rsa']['n']);
$this->Exponent = base64_encode($complem['rsa']['e']);
} else {
- echo "Error: No se puede leer el almacén de certificados o la clave no es la correcta.\n";
+ echo "Error: No se puede leer el almacén de certificados o la clave no es la correcta [$clavecertificado].\n";
exit;
}
@@ -125,8 +125,11 @@ public function firmar($certificadop12, $clavecertificado, $xmlsinfirma, $tipodo
public function insertaFirma($xml)
{
- if (is_null($this->publicKey) || is_null($this->privateKey)) {
- return $xml;
+ if (is_null($this->publicKey) || is_null($this->privateKey) || is_null($xml)) {
+ tools_reply([
+ "Status" => 400,
+ "text" => "Al firmar, el documento o las claves no pueden estar vacías",
+ ]);
}
// Canoniza todo el documento para el digest
@@ -167,6 +170,33 @@ public function insertaFirma($xml)
}
$certIssuer = implode(', ', array_reverse($certIssuer));
+
+ $startPos = strpos($xml, "", 0);
+ $clave = substr($xml, $startPos+7, 50);
+
+ $issuerCedula = $certData['subject']['serialNumber'];
+ $issuerCedula = preg_replace('/^(CPF|CPJ)/', '', $issuerCedula);
+ $issuerCedula = str_replace('-', '', $issuerCedula);
+ $issuerCedula = ltrim($issuerCedula, '0');
+ if(strpos($clave, $issuerCedula) === false){
+ tools_reply([
+ "Status" => 400,
+ "text" => "El número de cédula del certificado ($issuerCedula) no coincide con el indicado en la CLAVE en el XML ($clave)",
+ ]);
+ }
+
+ $startPos = strpos($xml, "", 0);
+ $startPos = strpos($xml, "", $startPos);
+ $startPos = strpos($xml, "", $startPos);
+ $endPos = strpos($xml, "", $startPos);
+ $emisorNumero = substr($xml, $startPos+8, $endPos -($startPos+8));
+
+ if($emisorNumero !== $issuerCedula){
+ tools_reply([
+ "Status" => 400,
+ "text" => "El número de cédula del certificado ($issuerCedula) no coincide con el indicado en el EMISOR en el XML ($emisorNumero)",
+ ]);
+ }
if (strpos($certData['serialNumber'], "0x") === false) {
// https://bugs.php.net/bug.php?id=77411
@@ -175,7 +205,7 @@ public function insertaFirma($xml)
$serialNumber = stringHex2StringDec($certData['serialNumber']);
}
- $prop = '' .
+ $prop = '' .
''.
'' . $signTime1 . '' .
''.
diff --git a/api/core/params.php b/api/core/params.php
index 60210784..20258df1 100644
--- a/api/core/params.php
+++ b/api/core/params.php
@@ -36,37 +36,31 @@ function params_get($p, $def = false)
}
/**
- * Set config parameters
+ * Set config parameters.
+ * - When $val is array, performs a bulk set.
+ * - $override=true overwrites existing keys; false preserves existing values.
+ * Returns the persisted value(s) from $params.
*/
-function params_set($p, $val = false, $override = false)
+function params_set($p, $val = false, $override = true)
{
global $params;
if (is_array($val)) {
foreach ($val as $vv => $v) {
- _params_set($vv, $v, $override);
+ if ($override || !array_key_exists($vv, $params)) {
+ $params[$vv] = $v;
+ }
}
- } else
- _params_set($p, $val, $override);
-
- return $params[$p];
-}
-
-/**
- * Helper function to actually store the params
- */
-function _params_set($p, $val = false, $override = false)
-{
- global $params;
-
- if (isset($params[$p]) && $override)
- $params[$p] = $val;
- else
+ // Return the slice that reflects what is now stored
+ return array_intersect_key($params, $val);
+ }
+ if ($override || !array_key_exists($p, $params)) {
$params[$p] = $val;
-
- return $params[$p];
+ }
+ return array_key_exists($p, $params) ? $params[$p] : null;
}
+
/**
* Verify request and set default values when required
* @todo Verify possible options for each a|b|c etc...
diff --git a/api/core/tools.php b/api/core/tools.php
index 64c4d821..166be5d7 100644
--- a/api/core/tools.php
+++ b/api/core/tools.php
@@ -18,80 +18,92 @@
function tools_reply($response, $killMe = false)
{
+ $httpStatus = 200;
switch ($response) {
case ERROR_USERS_NO_VALID:
- http_response_code(400);
+ $httpStatus = 400;
$response = "Usuario no válido";
$killMe = true;
break;
case ERROR_USERS_WRONG_LOGIN_INFO:
- http_response_code(401);
+ $httpStatus = 401;
$response = "Información de acceso incorrecta";
$killMe = true;
break;
case ERROR_USERS_NO_VALID_SESSION:
- http_response_code(440);
+ $httpStatus = 440;
$response = "Sesión no válida o expirada";
$killMe = true;
break;
case ERROR_USERS_ACCESS_DENIED:
- http_response_code(403);
+ $httpStatus = 403;
$response = "Acceso denegado";
$killMe = true;
break;
case ERROR_USERS_EXISTS:
- http_response_code(409);
+ $httpStatus = 409;
$response = "El usuario ya existe";
$killMe = true;
break;
default:
- http_response_code(200);
+ $httpStatus = 200;
}
if (is_array($response) && isset($response['Status'])) {
- switch ($response['Status']) {
- case 'error':
- http_response_code(500);
- $killMe = true;
- break;
- case 'ok':
- http_response_code(200);
- break;
- default:
- http_response_code(400);
- $killMe = true;
+ // propagar el codigo de estado HTTP cuando se recibe en el error
+ if (is_numeric($response['Status'])) {
+ $httpStatus = $response['Status'];
+ $killMe = $response['Status'] >= 299;
+ if(is_array($response['text']) && count($response['text']) > 1) {
+ // cuando la respuesta es un array de Hacienda, el primer elemento es el código de estado
+ // y el resto es el mensaje
+ unset($response['text'][0]);
+ }
+ } else {
+ switch ($response['Status']) {
+ case 'error':
+ $httpStatus = 500;
+ $killMe = true;
+ break;
+ case 'ok':
+ $httpStatus = 200;
+ break;
+ default:
+ $httpStatus = 400;
+ $killMe = true;
+ }
+
}
$response = $response['text'];
}
-
+
if ($killMe) {
if (is_string($response)) {
$response = "ERROR: " . $response;
}
} else {
- http_response_code(200);
+ $httpStatus = 200;
}
- if (params_get('replyType', 'json') == 'json') {
- _tools_reply(tools_returnJson(array(
+ if (conf_get('mode', 'core', 'web') == 'cli') {
+ throw new Exception(json_encode(array(
'status' => ($killMe ? 'error' : 'ok'),
- 'resp' => $response
+ 'resp' => $response,
+ 'httpStatus' => $httpStatus
)));
- # There will be other reply types soon...
- //}
- //elseif(params_get('replyType', 'json') == 'plain')
- //{
- # Reply all other replyTypes
- } else {
- if (conf_get('mode', 'core', 'web') == 'cli')
- $response .= "\n";
-
- _tools_reply($response);
}
+
+ _tools_reply(tools_returnJson(array(
+ 'status' => ($killMe ? 'error' : 'ok'),
+ 'resp' => $response,
+ 'httpStatus' => $httpStatus
+ )));
+
}
function _tools_reply($response)
{
+
print $response;
# This really should not be here, but so far, it should do
@@ -107,10 +119,11 @@ function _tools_reply($response)
function tools_returnJson($response, $addHeaders = true)
{
if ($addHeaders && conf_get('mode', 'core', 'web') != 'cli') {
+ http_response_code($response['httpStatus']);
header('Content-Type: text/html; charset=utf-8');
header('Content-Type: application/json');
}
-
+
return json_encode($response);
}
diff --git a/api/modules/db/module.php b/api/modules/db/module.php
index d1b93b1e..990d4749 100644
--- a/api/modules/db/module.php
+++ b/api/modules/db/module.php
@@ -48,7 +48,7 @@
*/
function db_bootMeUp()
{
- db_Connect();
+ conectarBd();
}
//! Test if the connection is good, just for debug
@@ -65,21 +65,24 @@ function db_allGood()
return true;
}
-function db_Connect()
+function conectarBd()
{
global $dbConn;
# Create connection
- grace_debug("config['db']['name']: ");
- grace_debug(conf_get('name', 'db'));
- grace_debug("config['db']['pwd']: ");
- grace_debug(conf_get('pwd', 'db'));
- grace_debug("config['db']['user']: ");
- grace_debug(conf_get('user', 'db'));
- grace_debug("config['db']['host']: ");
- grace_debug(conf_get('host', 'db'));
+ $dbConfig = [
+ 'host' => conf_get('host', 'db'),
+ 'port' => conf_get('port', 'db'),
+ 'user' => conf_get('user', 'db'),
+ 'pwd' => conf_get('pwd', 'db'),
+ 'name' => conf_get('name', 'db')
+ ];
+
+ grace_debug(json_encode($dbConfig));
+
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
- @$dbConn = new mysqli(conf_get('host', 'db'), conf_get('user', 'db'), conf_get('pwd', 'db'), conf_get('name', 'db'));
+
+ @$dbConn = new mysqli($dbConfig['host'], $dbConfig['user'], $dbConfig['pwd'], $dbConfig['name'], $dbConfig['port']);
# Check connection
if ($dbConn->connect_error)
@@ -148,6 +151,5 @@ function db_query($q, $return = 1)
function db_escape($string = '')
{
global $dbConn;
-
return $dbConn->real_escape_string($string);
}
diff --git a/api/modules/files/module.php b/api/modules/files/module.php
index 88ecb309..a6f58c8c 100644
--- a/api/modules/files/module.php
+++ b/api/modules/files/module.php
@@ -156,7 +156,7 @@ function files_upload($type = 'attach', $finalName = false, $ext = false, $maxSi
global $user;
# Load the tool
- tools_useTool('ImageResize.php');
+ // tools_useTool('ImageResize.php');
grace_debug("Uploading a file");
diff --git a/api/modules/users/module.php b/api/modules/users/module.php
index 597f7113..788184f4 100644
--- a/api/modules/users/module.php
+++ b/api/modules/users/module.php
@@ -380,7 +380,7 @@ function users_generateSessionKey($idUser)
db_query($q, 0);
modules_loader("crypto", "crypto.php");
- $sessionKey = crypto_encrypt(password_hash(time() * rand(0, 1000), PASSWORD_DEFAULT));
+ $sessionKey = crypto_encrypt(password_hash(time() * rand(0, 1000), PASSWORD_ARGON2ID));
$q = sprintf("INSERT INTO sessions (idUser, sessionKey, ip, lastAccess) "
. "VALUES('%s', '%s', '%s', '%s')", db_escape($idUser), $sessionKey, db_escape($_SERVER['REMOTE_ADDR']), time());
diff --git a/api/modules/wirez/messagesSend.php b/api/modules/wirez/messagesSend.php
index 2b8330ba..bbde2186 100644
--- a/api/modules/wirez/messagesSend.php
+++ b/api/modules/wirez/messagesSend.php
@@ -75,7 +75,7 @@ function wirez_conversationsCreate($idUser, $subject, $idRecipient)
db_escape($idUser),
db_escape($idRecipient),
$timestamp,
- db_escape($subject);
+ db_escape($subject));
$r = db_query($insert, 0);
diff --git a/api/tools/ValidadorXML.php b/api/tools/ValidadorXML.php
new file mode 100644
index 00000000..af2dc669
--- /dev/null
+++ b/api/tools/ValidadorXML.php
@@ -0,0 +1,148 @@
+level) {
+ case LIBXML_ERR_WARNING:
+ $r["tipo"] = "warning";
+ break;
+ case LIBXML_ERR_ERROR:
+ $r["tipo"] = "error";
+ break;
+ case LIBXML_ERR_FATAL:
+ $r["tipo"] = "fatal_error";
+ break;
+ }
+ $r["mensaje"] = $error->message;
+
+ return $r;
+ }
+
+ /**
+ * Get all libxml errors and clear them
+ *
+ * @return array
+ */
+ private static function libxml_display_errors(): array
+ {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+ $str_errors = [];
+ foreach ($errors as $error) {
+ $str_errors[] = self::libxml_display_error($error);
+ }
+
+ return $str_errors;
+ }
+
+ /**
+ * Valida el XML contra el XSD
+ *
+ * @param string $contenido_xml - XML sin firmar (plain text, not base64)
+ * @param string $consecutivo - Consecutivo del documento
+ * @param string $version - Versión del schema (4.3 o 4.4)
+ * @return object Resultado de la validación
+ */
+ public static function validateXml(string $contenido_xml, string $consecutivo, $version = "4.4"): object
+ {
+ $tipo_doc = substr($consecutivo, 8, 2);
+ $baseFolder = conf_get('coreInstall', 'modules', '');
+ $baseFolder = preg_replace('/\/api\/$/', '/www/', $baseFolder);
+ $schemas = [
+ "01" => "$baseFolder/xsd/FacturaElectronica_V$version.xsd",
+ "02" => "$baseFolder/xsd/FacturaCompra_V$version.xsd",
+ "03" => "$baseFolder/xsd/NotaCreditoElectronica_V$version.xsd",
+ "04" => "$baseFolder/xsd/TiqueteElectronico_V$version.xsd",
+ "05" => "$baseFolder/xsd/MensajeHacienda_V$version.xsd",
+ ];
+
+ if (empty($contenido_xml) || empty($tipo_doc)) {
+ return (object) [
+ "status" => "error",
+ "message" => "Error: falta el contenido del XML o el tipo de documento ($tipo_doc) al validar.",
+ ];
+ }
+
+ if (!array_key_exists($tipo_doc, $schemas)) {
+ return (object) [
+ "status" => "error",
+ "message" => "Error: no se tiene un esquema para el tipo $tipo_doc v$version",
+ ];
+ }
+
+ if (!file_exists($schemas[$tipo_doc])) {
+ return (object) [
+ "status" => "error",
+ "message" => "Error: no existe el archivo XSD " . $schemas[$tipo_doc] . " definido para el tipo $tipo_doc, v$version",
+ ];
+ }
+
+ libxml_use_internal_errors(true);
+ $xml = new DOMDocument();
+ $xml->loadXML($contenido_xml);
+
+ if (!$xml->schemaValidate($schemas[$tipo_doc])) {
+ $errors = self::libxml_display_errors();
+ $r = [
+ "status" => "error",
+ "schema" => $schemas[$tipo_doc],
+ "message" => $errors,
+ "xml" => $contenido_xml
+ ];
+ if (count($errors) === 0) {
+ $r["status"] = "ok";
+ $r["message"] = "El XML es válido";
+ unset($r["xml"]);
+ }
+ return (object) $r;
+ }
+
+ return (object) [
+ "status" => "ok",
+ "message" => "El XML es válido",
+ ];
+ }
+ /**
+ * Get the list of supported document types
+ *
+ * @return array
+ */
+ public static function getSupportedDocumentTypes(): array
+ {
+ return [
+ "01" => "Factura Electrónica",
+ "02" => "Factura de Compra",
+ "03" => "Nota de Crédito Electrónica",
+ "04" => "Tiquete Electrónico",
+ "05" => "Mensaje Hacienda",
+ ];
+ }
+
+ /**
+ * Get the document type from consecutivo
+ *
+ * @param string $consecutivo
+ * @return string
+ */
+ public static function getDocumentTypeFromConsecutivo(string $consecutivo): string
+ {
+ return substr($consecutivo, 8, 2);
+ }
+}
diff --git a/www/settings.php b/www/settings.php
index 406d6383..2469bff4 100644
--- a/www/settings.php
+++ b/www/settings.php
@@ -46,6 +46,8 @@
$config['db']['user'] = getenv('DB_USERNAME');
# Database host
$config['db']['host'] = getenv('DB_HOST');
+# Database port
+$config['db']['port'] = getenv('DB_PORT') ?: 3306; // Default to 3306 if not set
##############################################################################
#
# Crypto Keys
diff --git a/www/xsd/FacturaElectronica_V4.4.xsd b/www/xsd/FacturaElectronica_V4.4.xsd
index d042b8ed..ddb53060 100644
--- a/www/xsd/FacturaElectronica_V4.4.xsd
+++ b/www/xsd/FacturaElectronica_V4.4.xsd
@@ -1 +1,3250 @@
-
Elemento Raiz de la Facturacion Electrónica
Corresponde a la clave del comprobante. Es un campo fijo de cincuenta posiciones y se tiene que utilizar para
la consulta del código QR
Se debe indicar el número de cedula de identificación del proveedor de sistemas que esté utilizando para la emisión de comprobantes
electrónicos
Se debe de indicar el código de la actividad económica inscrita a la cual corresponde el comprobante que se está generando
Se debe de indicar el código de la actividad económica inscrita del receptor a la cual corresponden los bienes o servicios que se le están
facturando al receptor en caso de ser requerido para un crédito o un gasto deducible.
Numeración consecutiva del comprobante
Emisor del documento
Receptor del documento
Condiciones de la venta: 01 Contado, 02 Crédito, 03 Consignación, 04 Apartado, 05 Arrendamiento con opción de compra, 06 Arrendamiento en función financiera, 07 Cobro a favor de un tercero, 08 servicxios prestados al estado a credito, 09 pago del servicio prestado al estado,10 venta a crédito hasta 90 dias,11 pago de venta a crédito en IVA hasta 90 dias, 99 Otros
Contado
Crédito
Consignación
Apartado
Arrendamiento con opción de compra
Arrendamiento en función financiera
Cobro a favor de un tercero
Servicios prestados al Estado a crédito
Venta a crédito en IVA hasta 90 días (Artículo 27, LIVA)
Venta Mercancía No Nacionalizada
Venta Bienes Usados No Contribuyente
Arrendamiento Operativo
Arrendamiento Financiero
Otros
Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota
5. Se debe describir puntualmente la condición de la venta utilizada.
Plazo del crédito, es obligatorio cuando la venta del producto o prestación del servicio sea a crédito
Detalle del Servicio,
Mercancía u otro
Cada línea del detalle de la mercancia o servicio prestado.
Número de línea del detalle
Código de Producto/servicio
Cantidad
Unidad de medida
Este campo se utilizará para identificar el tipo de transacción
que se realizará.
Unidad de medida comercial
Detalle de la mercancia transferida o servicio prestado
Número de VIN o Serie
del medio de transporte
Se refiere al respectivo número de registro del Ministerio de Salud
Código de la presentación del medicamento.
Tipo complejo que representa cada línea del detalle de los componentes de un surtido, paquete o combinación de productos. Se debe utilizar exclusivamente cuando en la línea de detalle se está facturando un paquete, surtido o combo, entendido como la combinación de más de dos productos con diferentes códigos de producto/servicio.
Tipo complejo que representa cada línea del detalle del surtido
Código de Producto
/Servicio componente de Surtido
Código del producto del vendedor
Código del producto del comprador
código del producto asignado por la industria
código de uso interno
Otros
Es un número decimal compuesto por 13 enteros y 3 decimales.
Nodo utilizado para indicar una unidad de medida que nace del propio giro comercial del establecimiento, no es una cantidad estandarizada de una determinada magnitud física, definida y adoptada por convención o por ley ejemplo: "1
Tarima"
Detalle de la mercancía transferida o servicio prestado incluido en el surtido
Se obtiene de la multiplicación del campo "Cantidad componente de surtido" por el campo "Precio unitario componente de surtido".
Se puede incluir un máximo de 5 repeticiones de
descuentos, cada descuento adicional se calcula sobre la base menos el descuento anterior.
Validación: Se deberá incluir un valor igual o menor al del
"Monto total componente surtido"
Este campo será de condición obligatoria, cuando se incluya información en el campo "Monto de descuentos concedidos al componente de surtido
Este campo será de condición obligatoria, cuando se utilice el código 99 de la Nota 20
Validación: En caso de utilizarse el código 99, se verificará que este campo se encuentre en el comprobante, caso contrario se rechazará. Además, deberá contener mínimo 3 caracteres y un máximo de 80
Se obtiene de la resta del campo "Monto total componente surtido" menos "Monto de descuentos concedidos al componente de surtido"
En este campo se indicará si el Impuesto al Valor Agregado fue cobrado a nivel de fábrica, por lo que deberá ser utilizado únicamente por los obligados tributarios a realizar el pago de esta forma.
Se convierte en obligatorio cuando el IVA se cobra o se cobró a nivel de fábrica.
Al hacer uso del presente campo el producto se entenderá exento para el código 02, por lo cual no deberá llenar el subnodo de impuestos para el cálculo del IVA.
▪Para el código 01 el emisor puede separar los impuestos
que está cobrando en la fábrica.
Venta de bienes con IVA según el sistema especial de determinación de IVA a nivel de fábrica (Se utiliza cuando se está cobrando el IVA a nivel de fábrica
Ventas exentas según el sistema especial de determinación de IVA a nivel de fábrica, mayorista y aduanas (se utiliza cuando el producto se encuentra exento ya que el bien soporto el cobro de impuestos a nivel de fábrica).
Este campo será de condición obligatoria, cuando el producto este gravado con algún impuesto. Se obtiene de la suma entre el campo "Subtotal componente del surtido", más el impuesto selectivo de consumo (02), el Impuesto específico de Bebidas Alcohólicas (04) y el Impuesto Específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador (05), cuando corresponda. Este campo se podrá editar cuando se seleccione en el campo "IVA cobrado a nivel de fábrica" el Código 01 o en el campo de "Código del impuesto" el código 07. Validación: En caso de utilizarse el código 01, en el campo de IVA cobrado a nivel de fábrica, se verificará que este campo se encuentre en el comprobante, caso contrario se rechazará. Además, se deberá incluir un valor mayor a "cero".
Ver nota 8. Es un campo fijo de dos posiciones.
Al utilizar el código de Naturaleza del Descuento 01 correspondiente a "Regalías" o 03 de "Bonificaciones" y el código de impuesto 01, se debe utilizar para el cálculo del impuesto el campo denominado "Monto total componente de surtido" y la "Tarifa del Impuesto al Valor Agregado para componente de surtido"
Validación: Se verificará el cumplimiento de la nota 8
Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota 8. Se debe describir puntualmente el impuesto utilizado.
Validación: Deberá contener mínimo 5 caracteres y un máximo de 100
Se convierte en obligatorio cuando se usa el código 01 de impuestos de surtido
Validación: Se verificará el cumplimiento de nota 8.1. cuando se utilice el código 01 de campo código del impuesto.
Este campo es de condición obligatoria, cuando el componente este gravado con alguna tarifa de impuesto, según corresponda. Debe de expresarse el porcentaje como número entero (Ejemplo: la tarifa del 13% se debe de reflejar como 13, la tarifa del 1% como 1, o bien la tarifa del 0.5% como 0.5)
Datos para Impuestos Específicos para componente de surtido
Cantidad de la unidad de medida a utilizar para componente de surtido. Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 04, 05 y 06 de la nota 8
Porcentaje en componente de surtido. Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8. Debe de expresarse el porcentaje como número entero (Ejemplo: la tarifa del 13% se debe de reflejar como 13, la tarifa del 1% como 1, o bien la tarifa del 0.5% como 0.5)
Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8
Este campo se obtiene de multiplicar la "Cantidad de la unidad de medida a utilizar" por el "Porcentaje"
Volumen por Unidad de Consumo componente de surtido. Este campo es de condición obligatoria, cuando se utilice el código de impuesto 05 de la nota 8
Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 04, 05 y 06 de la nota 8
Este campo será de condición obligatoria, cuando el componente este gravado con algún impuesto. Es un número decimal compuesto por 13 enteros y 5 decimales
Precio Unitario
Se obtiene de multiplicar el campo cantidad por el campo precio unitario
Se obtiene de la resta del campo monto total menos monto de descuento concedido
En este campo se indicará si el Impuesto al Valor Agregado fue cobrado a nivel de fábrica, por lo que deberá ser utilizado únicamente por los obligados tributarios a realizar el pago de
esta forma. Se convierte en obligatorio cuando el IVA se cobra o se cobró a nivel de fábrica. Al hacer uso del presente campo el producto se entenderá exento para el código 02, por lo cual no deberá llenar el subnodo de impuestos para el cálculo del IVA. Para el código 01 el emisor puede separar los impuestos
que está cobrando en la fábrica.
Venta de bienes con IVA según el sistema especial de determinación de IVA a nivel de fábrica (Se utiliza cuando se está cobrando el IVA a nivel de fábrica
Ventas exentas según el sistema especial de determinación de IVA a nivel de fábrica, mayorista y aduanas (se utiliza cuando el producto se encuentra exento ya que el bien soporto el cobro de impuestos a nivel de fábrica).
Este campo será de condición obligatoria, cuando el
producto/ servicio este gravado con algún impuesto. Se obtiene de la suma entre el campo "Subtotal", más el impuesto selectivo de consumo (02), el Impuesto específico de Bebidas Alcohólicas (04), el Impuesto Específico sobre las bebidas envasadas sin contenido alcohólico y jabones de
tocador (05) y el impuesto al cemento (12), cuando corresponda.
Este campo se podrá editar cuando se seleccione en el campo "IVA cobrado a nivel de fábrica" el Código 01 o en el campo de "Código del impuesto" el código 07.
Cuando el producto o servicio este gravado con algún impuesto se debe indicar cada uno de ellos.
Impuestos Asumidos por el Emisor o cobrado a Nivel de Fábrica
Este monto se obtiene al restar el campo “Monto del Impuesto” menos “Monto del Impuesto Exonerado” o el
campo “Impuestos Asumidos por el Emisor o cobrado a Nivel de Fábrica” cuando corresponda
Se calcula de la siguiente manera:
se obtiene de la sumatoria de los campos “Subtotal”, “Impuesto Neto”.
Información sobre otros cargos
Total de los servicios gravados con IVA
Total de los servicios exentos de IVA
Total servicios exonerados del IVA
Este campo será de condición obligatoria, cuando se seleccionen códigos CAByS que correspondan a un servicio y el servicio sea No Sujeto de IVA
Total mercancias gravadas con IVA
Total mercancias exentas de IVA
Total mercancías exoneradas del IVA
Este campo será de condición obligatoria, cuando se seleccionen códigos CAByS que correspondan a una mercancía y la mercancía sea No Sujeta de IVA
Total gravado. se obtiene de la suma del total servicios gravados con IV + total mercancias gravadas con IV
Total Exento, se obtiene de la suma de los campos total servicios exentos IV mas total mercancias exentas IV
Se obtiene de la suma de los campos "total servicios exonerados de IVA" mas "total de mercancías exoneradas del IVA".
Se obtiene de la suma de los campos "Total servicios No Sujetos de IVA" mas "Total mercancías No Sujetas de IVA".
Se obtiene de la sumatoria de los campos “total gravado”, “total exento”, “Total Exonerado” y “Total No Sujeto
Se obtiene de la suma de todos los campo de monto de descuento concedido
Se obtiene de la resta de los campos total venta menos total descuento
Tipo complejo que contiene los montos desglosados por impuesto cobrado en el comprobante electrónico.
Indicará los códigos de impuesto registrados en las líneas de detalle.
Se obtiene de la sumatoria del monto por código de impuesto cobrado en el comprobante electrónico
Se obtiene de la suma de todos campos monto del impuesto
Este campo es de condición obligatoria, cuando existen producto/servicio gravados con algún impuesto en las líneas de detalle que sean asumidos por el emisor
IVA Devuelto
Total Otros Cargos
Corresponde al medio de pago empleado: 01 - Efectivo, 02 - Tarjeta, 03 - Cheque, 04 - Transferencia - depósito bancario, 05 - Recaudado por terceros, 06 - SINPE MOVIL, 07 - Plataforma Digital, 99 - Otros
Efectivo
Tarjeta
Cheque
Transferencia - depósito bancario
Recaudado por terceros
SINPE MOVIL
Plataforma Digital
Otros
Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota 6. Se debe describir puntualmente el medio de pago utilizado
Se deberá detallar el monto correspondiente al tipo de pago seleccionado. Se volverá obligatorio cuando se utilice más de un medio de pago.
Se obtiene de la suma de los campos "total venta neta", "monto total del impuesto" y "total otros cargos" menos "total IVA devuelto", en caso de contar con dichos campos.
Tipo de documento de referencia
Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 10. Se debe describir puntualmente el tipo de documento utilizado
Clave numérica del comprobante electrónico o consecutivo del documento de referencia
Fecha de emisión del documento de referencia
Código de referencia. 01 Anula documento de referencia, 02 Corrige texto de documento de referencia, 04 Referencia a otro documento, 05 Sustituye comprobante provisional por contigencia, 99 Otros
Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 9. Se debe describir puntualmente el código de referencia utilizado
Razón de referencia
Elemento opcional que se puede utilizar para almacenar texto.
Código opcional para facilitar la identificación del elemento.
Elemento opcional que se puede utilizar para almacenar contenido estructurado.
Código opcional para facilitar la identificación del elemento.
Nombre o razon social
Campo condicional. Se convierte en carácter obligatorio cuando se
estén facturando códigosCAByS de bebidas alcohólicas según la Ley
8707. Contiene los datos del número de registro de bebidas
alcohólicas, suministrado por la Dirección General de Aduanas
En caso de que se cuente con nombre comercial debe indicarse
Debe cumplir con la siguiente estructura:
\s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*
Nombre o razon social
En caso de que se cuente con nombre comercial debe indicarse
Campo para incluir la direccion del extranjero, en caso de requerirse.
Este campo será de condición obligatoria, cuando el cliente lo requiera. Debe cumplir con la siguiente estructura:
\s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*
Tipo de identificación: 01 Cédula Física, 02 Cédula Jurídica, 03 DIMEX, 04 NITE, 05 Extranjero No Domiciliado, 06 No Contribuyente
Cedula Fisica
Cedula Juridica
DIMEX
NITE
Extranjero No Domiciliado
No Contribuyente
Número de identificación, el contribuyente debe estar inscrito ante la Administración Tributaria
Código del país
Número de teléfono
Tipo de documento de exoneración o autorización.
Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 10.1. Se debe describir puntualmente el tipo de documento o autorización utilizado
Número de documento de exoneración o autorización
Número de artículo que establece la exoneración o autorización
Número de inciso que establece la exoneración o autorización
Nombre de la institución o dependencia que emitió la exoneración
Ministerio de Hacienda
Ministerio de Relaciones Exteriores y Culto
Ministerio de Agricultura y Ganadería
Ministerio de Economía, Industria y Comercio
Cruz Roja Costarricense
Benemérito Cuerpo de Bomberos de Costa Rica
Asociación Obras del Espíritu Santo
Federación Cruzada Nacional de protección al Anciano (Fecrunapa)
Escuela de Agricultura de la Región Húmeda (EARTH)
Instituto Centroamericano de Administración de Empresas (INCAE)
Junta de Protección Social (JPS)
Autoridad Reguladora de los Servicios Públicos (Aresep)
Otros
Detalle Nombre de institución o dependencia que emitió la exoneración OTRO
Fecha y hora de la emisión del documento de exoneración o autorización.
Tarifa exonerada
Monto del impuesto exonerado
Código del impuesto: 01 Impuesto al valor agregado, 02 Impuesto Selectivo de Consumo, 03 Impuesto único a los combustivos, 04 Impuesto específico de bebidas alcohólicas, 05 Impuesto específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador, 06 Impuesto a los productos de tabaco, 07 IVA (cálculo especial), 08 IVA Regimen de Bienes Usados (Factor), 12 Impuesto Especifico al cemento, 99 Otros
Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 8. Se debe describir puntualmente el impuesto
utilizado
En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”, no se deberá utilizar este
campo, ya que el impuesto se calcula como la suma de los montos de impuestos individuales de las líneas de detalle de los componentes del surtido que se deben incluir en estos
casos. La eventual validación de la consistencia de los impuestos calculados y aplicación de tarifas se hará sobre las líneas individuales de detalle.
En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”, no se deberá utilizar este campo, ya que el impuesto se calcula como la suma de los montos de impuestos individuales de las líneas de detalle de componentes del surtido que se deben incluir en estos casos. La eventual validación de la consistencia de los impuestos calculados y aplicación de tarifas se hará sobre las líneas individuales de detalle.
Este campo es de condición obligatoria, cuando el producto/servicio posea un factor para su cálculo.
Cuando en el código de impuesto se defina IVA Bienes Usados se deberá utilizar este campo con el factor establecido por el Ministerio de Hacienda
Tipo complejo con el detalle para calcular impuestos específicos no tarifarios. Este campo es de condición obligatoria, cuando se utilicen
los códigos de impuesto 03, 04, 05, 06 de la nota 8 y agrupará los campos requeridos para el cálculo de estos impuestos
En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”, no se deberá utilizar este
campo, para los códigos de impuesto 04, 05, 06 de la nota 8, ya que el impuesto se calcula como la suma de los montos de impuestos individuales de las líneas de detalle de
componentes del surtido que se deben incluir en estos casos
Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8
Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8 Este campo se obtiene de multiplicar la “Cantidad de la unidad de medida a utilizar” por el “Porcentaje”
Este campo es de condición obligatoria, cuando se utilice el código de impuesto 05 de la nota 8
Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 03, 04, 05 y 06 de la nota 8
Monto del impuesto
Será obligatorio para las líneas de detalle que utilicen uno de
los códigos de producto/servicio de "surtidos" que estén
habilitados en el CAByS.
En el caso de la inclusión de paquetes, surtidos o combos,
entendidos como la combinación de más de dos productos
con diferentes códigos de producto/servicio, se debe
seleccionar el código 03 "Código del producto asignado por la
industria" de la nota 12 e incluir en el campo "código" el
respectivo código "SKU", GTIN o equivalentes, con el que el
paquete este identificado en la industria. Estos códigos deben
ser verificables en los catálogos disponibles en la industria.
Código del producto del vendedor
Código del producto del comprador
código del producto asignado por la industria
código de uso interno
Otros
Será obligatorio para las líneas de detalle que utilicen uno de
los códigos de producto/servicio de "surtidos" que estén
habilitados en el CAByS.
Monto de descuento concedido. Este campo será de condición obligatoria, cuando se indique
un descuento, en el campo "Código del descuento"
Este campo será de condición obligatoria, cuando se incluya
información en el campo "monto de descuentos concedidos"
Será obligatorio en caso de utilizar el código 99 de "Otros"
de la nota 20. Se debe describir puntualmente el descuento
utilizado
Naturaleza del descuento, que es obligatorio si existe descuento
Se verificará el cumplimiento de la nota 16.
Además, cuando se seleccione el código 04, 08, 09 y 10 de la nota 16 en “Tipo de documento otros cargos” y no se cuente con una línea de servicio o producto, no es obligatorio usar el nodo “Detalle de la mercancía o servicio prestado”.
Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 16. Se debe describir puntualmente el tipo de documento utilizado
Nombre o razón social del receptor
Detalle de otros cargos
En el caso que el cargo posea un porcentaje o monto para su cálculo se debe de indicar el mismo
Monto del cargo
Código de la moneda
Tipo de cambio
Tipo de dato decimal para representar los valores de dinero.
Tipo de dato String que solo permite el uso de números con un largo de 50.
Tipo de dato String que solo permite el uso de números con un largo de 20
Unidades de Medida basadas en el estándar RTC 443:2010
uno (índice de refracción)
minuto
segundo
grado Celsius
1 por metro
Ampere
ampere por metro
ampere por metro cuadrado
Activo Virtual
Alquiler de uso habitacional
Alquiler de uso comercial
bel
Becquerel
coulomb
coulomb por kilogramo
coulomb por metro cuadrado
coulomb por metro cúbico
Cajuela de café
Candela
candela por metro cuadrado
Comisiones
centímetro
cuartillos de café
día
electronvolt
farad
farad por metro
fanega de café
Gramo
Galón
gray
gray por segundo
hora
henry
henry por metro
hertz
Intereses
Joule
joule por kilogramo kelvin
joule por mol kelvin
joule por kelvin
joule por kilogramo
joule por metro cúbico
joule por mol
Kelvin
katal
katal por metro cúbico
Kilogramo
kilogramo por metro cúbico
Kilometro
kilovatios
Kilovatios por hora
litro
lumen
pulgada
lux
Metro
metro por segundo
metro por segundo cuadrado
metro cuadrado
metro cúbico
minuto
mililitro
Milímetro
Mol
mol por metro cúbico
newton
newton por metro
newton metro
neper
grado
Otro tipo de servicio
Se debe indicar la descripción de la medida a utilizar
Onzas
pascal
pascal segundo
Quintal
radián
radián por segundo
radián por segundo cuadrado
Segundo
siemens
Servicios Profesionales
Servicios personales
estereorradián
Servicios técnicos
sievert
tesla
tonelada
unidad de masa atómica unificada
unidad astronómica
Unidad
volt
volt por metro
Watt
watt por metro kevin
watt por metro cuadrado estereorradián
watt por metro cuadrado
watt por estereorradián
weber
ohm
Tipos de transacción para el caso de transacciones con tratamientos tributarios específicos
Venta Normal de Bienes y Servicios (Transacción General)
Mercancía de Autoconsumo exento
Mercancía de Autoconsumo gravado
Servicio de Autoconsumo exento
Servicio de Autoconsumo gravado
Cuota de afiliación
Cuota de afiliación Exenta
Bienes de Capital para el emisor
Bienes de Capital para el receptor
Bienes de Capital para el emisor y el receptor
Bienes de capital de autoconsumo exento para el emisor
Bienes de capital sin contraprestación a terceros exento para el emisor
Sin contraprestación a terceros
Los tipos de descuentos a utilizar en el campo "Código del Descuento" del Descuento
Descuento por Regalía
Descuento por Regalía IVA Cobrado al Cliente
Descuento por Bonificación
Descuento por volumen
Descuento por Temporada (estacional)
Descuento promocional
Descuento Comercial
Descuento por frecuencia
Descuento sostenido
Otros descuentos
Código del impuesto: 01 Impuesto al valor agregado, 02 Impuesto Selectivo de Consumo, 03 Impuesto único a los combustivos, 04 Impuesto específico de bebidas alcohólicas, 05 Impuesto específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador, 06 Impuesto a los productos de tabaco, 07 IVA (cálculo especial), 08 IVA Regimen de Bienes Usados (Factor), 12 Impuesto Especifico al cemento, 99 Otros
Impuesto al Valor Agregado
Impuesto Selectivo de Consumo
Impuesto unico a los combustivos
Impuesto especifico de bebidas alcohólicas
impuesto especifico sobre las bebidas envasadas sin contenido alcoholico y jabones de tocador
impuesto a los productos de tabaco
IVA (cálculo especial)
IVA Régimen de Bienes Usados (Factor)
Impuesto Especifico al Cemento
Otros
Cuando se trata del IVA las tarifas y códigos a utilizar son las siguientes
Tarifa 0% (Artículo 32, num 1, RLIVA)
Tarifa reducida 1%
Tarifa reducida 2%
Tarifa reducida 4%
Transitorio 0%
Transitorio 4%
Tarifa transitoria 8%
Tarifa general 13%
Tarifa reducida 0.5%
Tarifa Exenta
Tarifa 0% sin derecho a crédito
Tipo de documento de exoneración o de autorización.
Compras autorizadas por la Dirección General de Tributación
Ventas exentas a diplomáticos
Autorizado por Ley Especial
Exenciones Dirección General de Hacienda Autorización Local Genérica
Exenciones Dirección General de Hacienda Transitorio V (servicios de ingeniería, arquitectura, topografía obra civil)
Servicios turísticos inscritos ante el Instituto Costarricense de Turismo (ICT)
Transitorio XVII (Recolección, Clasificación, almacenamiento de Reciclaje y reutilizable)
Exoneración a Zona Franca
Exoneración de servicios complementarios para la exportación articulo 11 RLIVA
Órgano de las corporaciones municipales
Exenciones Dirección General de Hacienda Autorización de Impuesto Local Concreta
Otros
Tipo de documento otros cargos
Contribución parafiscal
Timbre de la Cruz Roja
Timbre de Benemérito Cuerpo de Bomberos de Costa Rica
Cobro de un tercero
Costos de Exportación
Impuesto de servicio 10%
Timbre de Colegios Profesionales
Depósitos de Garantía
Multas o Penalizaciones
Intereses Moratorios
Otros Cargos
Factura electrónica
Nota de debido electrónica
nota de crédito electrónica
Tiquete electrónico
Nota de despacho
Contrato
Procedimiento
Comprobante emitido en contigencia
Devolución mercadería
Comprobante electrónico rechazado por el Ministerio de Hacienda
Sustituye factura rechazada por el Receptor del comprobante
Sustituye Factura de exportación
Facturación mes vencido
Otros
Comprobante aportado por contribuyente de Régimen Especial.
Sustituye una Factura electrónica de Compra
Comprobante de Proveedor No Domiciliado
Nota de Crédito a Factura Electrónica de Compra
Nota de Débito a Factura Electrónica de Compra
Anula documento de referencia
Corrige texto de ocumento de referencia
Referencia a otro documento
Sustituye comprobante provisional por contigencia
Devolución de mercancía
Sustituye comprobante electrónico
Factura Endosada
Nota de crédito financiera
Nota de débito financiera
Proveedor No Domiciliado
Crédito por exoneración posterior a la facturación
Otros
\ No newline at end of file
+
+
+
+
+
+ Elemento Raiz de la Facturacion Electrónica
+
+
+
+
+
+
+ Corresponde a la clave del comprobante.
+ Es un campo fijo de cincuenta posiciones y se tiene que utilizar parala consulta del código QR
+
+
+
+
+
+
+ Se debe indicar el número de cedula de identificación del proveedor de sistemas
+ que esté utilizando para la emisión de comprobantes electrónicos
+
+
+
+
+
+
+
+
+
+
+
+ Se debe de indicar el código de la actividad económica inscrita a la cual corresponde
+ el comprobante que se está generando
+
+
+
+
+
+
+
+
+
+
+
+
+ Se debe de indicar el código de la actividad económica inscrita del receptor a la cual corresponden
+ los bienes o servicios que se le están facturando al receptor en caso de ser requerido para un crédito
+ o un gasto deducible.
+
+
+
+
+
+
+
+
+
+
+
+ Numeración consecutiva del comprobante
+
+
+
+
+
+ Emisor del documento
+
+
+
+
+ Receptor del documento
+
+
+
+
+
+ Condiciones de la venta:
+ 01 Contado,
+ 02 Crédito,
+ 03 Consignación,
+ 04 Apartado,
+ 05 Arrendamiento con opción de compra,
+ 06 Arrendamiento en función financiera,
+ 07 Cobro a favor de un tercero,
+ 08 servicxios prestados al estado a credito,
+ 09 pago del servicio prestado al estado,
+ 10 venta a crédito hasta 90 dias,
+ 11 pago de venta a crédito en IVA hasta 90 dias,
+ 99 Otros
+
+
+
+
+
+
+ Contado
+
+
+
+
+ Crédito
+
+
+
+
+ Consignación
+
+
+
+
+ Apartado
+
+
+
+
+ Arrendamiento con opción de compra
+
+
+
+
+ Arrendamiento en función financiera
+
+
+
+
+ Cobro a favor de un tercero
+
+
+
+
+ Servicios prestados al Estado a crédito
+
+
+
+
+ Venta a crédito en IVA hasta 90 días (Artículo 27, LIVA)
+
+
+
+
+ Venta Mercancía No Nacionalizada
+
+
+
+
+ Venta Bienes Usados No Contribuyente
+
+
+
+
+ Arrendamiento Operativo
+
+
+
+
+ Arrendamiento Financiero
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota 5.
+ Se debe describir puntualmente la condición de la venta utilizada.
+
+
+
+
+
+
+
+
+
+
+
+
+ Plazo del crédito, es obligatorio cuando la venta del producto o prestación del servicio sea a crédito
+
+
+
+
+
+
+
+
+
+
+
+ Detalle del Servicio, Mercancía u otro
+
+
+
+
+
+
+ Cada línea del detalle de la mercancia o servicio prestado.
+
+
+
+
+
+ Número de línea del detalle
+
+
+
+
+
+
+
+
+
+
+ Código de Producto/servicio
+
+
+
+
+
+
+
+
+
+
+
+ Cantidad
+
+
+
+
+
+
+
+
+
+
+ Unidad de medida
+
+
+
+
+
+ Este campo se utilizará para identificar el tipo de transacción que se realizará.
+
+
+
+
+
+
+
+
+
+
+ Unidad de medida comercial
+
+
+
+
+
+
+
+
+
+ Detalle de la mercancia transferida o servicio prestado
+
+
+
+
+
+
+
+
+
+
+
+ Número de VIN o Serie
+ del medio de transporte
+
+
+
+
+
+
+
+
+
+
+ Se refiere al respectivo número de registro del Ministerio de Salud
+
+
+
+
+
+
+
+
+
+ Código de la presentación del medicamento.
+
+
+
+
+
+
+
+
+
+ Tipo complejo que representa cada línea del detalle de los componentes de un surtido, paquete o combinación de productos. Se debe utilizar exclusivamente cuando en la línea de detalle se está facturando un paquete, surtido o combo, entendido como la combinación de más de dos productos con diferentes códigos de producto/servicio.
+
+
+
+
+
+ Tipo complejo que representa cada línea del detalle del surtido
+
+
+
+
+
+ Código de Producto/Servicio componente de Surtido
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Código del producto del vendedor
+
+
+
+
+ Código del producto del comprador
+
+
+
+
+ código del producto asignado por la industria
+
+
+
+
+ código de uso interno
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Es un número decimal compuesto por 13 enteros y 3 decimales.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nodo utilizado para indicar una unidad de medida que nace del propio giro comercial del establecimiento, no es una cantidad estandarizada de una determinada magnitud física, definida y adoptada por convención o por ley ejemplo: "1 Tarima"
+
+
+
+
+
+
+
+
+
+ Detalle de la mercancía transferida o servicio prestado incluido en el surtido
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Se obtiene de la multiplicación del campo "Cantidad componente de surtido" por el campo "Precio unitario componente de surtido".
+
+
+
+
+
+ Se puede incluir un máximo de 5 repeticiones de
+ descuentos, cada descuento adicional se calcula sobre la base menos el descuento anterior.
+
+
+
+
+
+
+
+ Validación: Se deberá incluir un valor igual o menor al del
+ "Monto total componente surtido"
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando se incluya información en el campo "Monto de descuentos concedidos al componente de surtido
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando se utilice el código 99 de la Nota 20
+ Validación: En caso de utilizarse el código 99, se verificará que este campo se encuentre en el comprobante, caso contrario se rechazará. Además, deberá contener mínimo 3 caracteres y un máximo de 80
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Se obtiene de la resta del campo "Monto total componente surtido" menos "Monto de descuentos concedidos al componente de surtido"
+
+
+
+
+
+ En este campo se indicará si el Impuesto al Valor Agregado fue cobrado a nivel de fábrica, por lo que deberá ser utilizado únicamente por los obligados tributarios a realizar el pago de esta forma.
+ Se convierte en obligatorio cuando el IVA se cobra o se cobró a nivel de fábrica.
+ Al hacer uso del presente campo el producto se entenderá exento para el código 02, por lo cual no deberá llenar el subnodo de impuestos para el cálculo del IVA.
+ ▪Para el código 01 el emisor puede separar los impuestos
+ que está cobrando en la fábrica.
+
+
+
+
+
+
+
+
+ Venta de bienes con IVA según el sistema especial de determinación de IVA a nivel de fábrica (Se utiliza cuando se está cobrando el IVA a nivel de fábrica
+
+
+
+
+
+ Ventas exentas según el sistema especial de determinación de IVA a nivel de fábrica, mayorista y aduanas (se utiliza cuando el producto se encuentra exento ya que el bien soporto el cobro de impuestos a nivel de fábrica).
+
+
+
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando el producto este gravado con algún impuesto. Se obtiene de la suma entre el campo "Subtotal componente del surtido", más el impuesto selectivo de consumo (02), el Impuesto específico de Bebidas Alcohólicas (04) y el Impuesto Específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador (05), cuando corresponda. Este campo se podrá editar cuando se seleccione en el campo "IVA cobrado a nivel de fábrica" el Código 01 o en el campo de "Código del impuesto" el código 07. Validación: En caso de utilizarse el código 01, en el campo de IVA cobrado a nivel de fábrica, se verificará que este campo se encuentre en el comprobante, caso contrario se rechazará. Además, se deberá incluir un valor mayor a "cero".
+
+
+
+
+
+
+
+
+ Ver nota 8. Es un campo fijo de dos posiciones.
+ Al utilizar el código de Naturaleza del Descuento 01 correspondiente
+ a "Regalías" o 03 de "Bonificaciones" y el código de impuesto 01,
+ se debe utilizar para el cálculo del impuesto el campo denominado "Monto total componente de surtido"
+ y la "Tarifa del Impuesto al Valor Agregado para componente de surtido"
+ Validación: Se verificará el cumplimiento de la nota 8
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota 8. Se debe describir puntualmente el impuesto utilizado.
+ Validación: Deberá contener mínimo 5 caracteres y un máximo de 100
+
+
+
+
+
+
+
+
+
+
+
+
+ Se convierte en obligatorio cuando se usa el código 01 de impuestos de surtido
+ Validación: Se verificará el cumplimiento de nota 8.1. cuando se utilice el código 01 de campo código del impuesto.
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando el componente este gravado con alguna tarifa de impuesto, según corresponda. Debe de expresarse el porcentaje como número entero (Ejemplo: la tarifa del 13% se debe de reflejar como 13, la tarifa del 1% como 1, o bien la tarifa del 0.5% como 0.5)
+
+
+
+
+
+
+
+
+
+
+ Datos para Impuestos Específicos para componente de surtido
+
+
+
+
+
+ Cantidad de la unidad de medida a utilizar para componente de surtido. Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 04, 05 y 06 de la nota 8
+
+
+
+
+
+
+
+
+
+
+ Porcentaje en componente de surtido. Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8. Debe de expresarse el porcentaje como número entero (Ejemplo: la tarifa del 13% se debe de reflejar como 13, la tarifa del 1% como 1, o bien la tarifa del 0.5% como 0.5)
+
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8
+ Este campo se obtiene de multiplicar la "Cantidad de la unidad de medida a utilizar" por el "Porcentaje"
+
+
+
+
+
+
+
+
+
+
+
+ Volumen por Unidad de Consumo componente de surtido. Este campo es de condición obligatoria, cuando se utilice el código de impuesto 05 de la nota 8
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 04, 05 y 06 de la nota 8
+
+
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando el componente este gravado con algún impuesto. Es un número decimal compuesto por 13 enteros y 5 decimales
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Precio Unitario
+
+
+
+
+ Se obtiene de multiplicar el campo cantidad por el campo precio unitario
+
+
+
+
+
+ Se obtiene de la resta del campo monto total menos monto de descuento concedido
+
+
+
+
+
+ En este campo se indicará si el Impuesto al Valor Agregado fue cobrado a nivel de fábrica, por lo que deberá ser utilizado únicamente por los obligados tributarios a realizar el pago de
+ esta forma. Se convierte en obligatorio cuando el IVA se cobra o se cobró a nivel de fábrica. Al hacer uso del presente campo el producto se entenderá exento para el código 02, por lo cual no deberá llenar el subnodo de impuestos para el cálculo del IVA. Para el código 01 el emisor puede separar los impuestos
+ que está cobrando en la fábrica.
+
+
+
+
+
+
+
+
+ Venta de bienes con IVA según el sistema especial de determinación de IVA a nivel de fábrica (Se utiliza cuando se está cobrando el IVA a nivel de fábrica
+
+
+
+
+
+ Ventas exentas según el sistema especial de determinación de IVA a nivel de fábrica, mayorista y aduanas (se utiliza cuando el producto se encuentra exento ya que el bien soporto el cobro de impuestos a nivel de fábrica).
+
+
+
+
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando el producto/ servicio este gravado con algún impuesto.
+ Se obtiene de la suma entre el campo "Subtotal", más
+ el impuesto selectivo de consumo (02),
+ el Impuesto específico de Bebidas Alcohólicas (04),
+ el Impuesto Específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador (05) y
+ el impuesto al cemento (12), cuando corresponda.
+ Este campo se podrá editar cuando se seleccione
+ en el campo "IVA cobrado a nivel de fábrica" el Código 01
+ o en el campo de "Código del impuesto" el código 07.
+
+
+
+
+
+ Cuando el producto o servicio este gravado con algún impuesto se debe indicar cada uno de ellos.
+
+
+
+
+ Impuestos Asumidos por el Emisor o cobrado a Nivel de Fábrica
+
+
+
+
+
+ Este monto se obtiene al restar el campo “Monto del Impuesto” menos “Monto del Impuesto Exonerado” o el
+ campo “Impuestos Asumidos por el Emisor o cobrado a Nivel de Fábrica” cuando corresponda
+
+
+
+
+
+
+ Se calcula de la siguiente manera:
+ se obtiene de la sumatoria de los campos “Subtotal”, “Impuesto Neto”.
+
+
+
+
+
+
+
+
+
+
+
+ Información sobre otros cargos
+
+
+
+
+
+
+
+
+ Total de los servicios gravados con IVA
+
+
+
+
+ Total de los servicios exentos de IVA
+
+
+
+
+ Total servicios exonerados del IVA
+
+
+
+
+ Este campo será de condición obligatoria, cuando se seleccionen códigos CAByS que correspondan a un servicio y el servicio sea No Sujeto de IVA
+
+
+
+
+ Total mercancias gravadas con IVA
+
+
+
+
+ Total mercancias exentas de IVA
+
+
+
+
+ Total mercancías exoneradas del IVA
+
+
+
+
+ Este campo será de condición obligatoria, cuando se seleccionen códigos CAByS que correspondan a una mercancía y la mercancía sea No Sujeta de IVA
+
+
+
+
+ Total gravado. se obtiene de la suma del total servicios gravados con IV + total mercancias gravadas con IV
+
+
+
+
+ Total Exento, se obtiene de la suma de los campos total servicios exentos IV mas total mercancias exentas IV
+
+
+
+
+ Se obtiene de la suma de los campos "total servicios exonerados de IVA" mas "total de mercancías exoneradas del IVA".
+
+
+
+
+ Se obtiene de la suma de los campos "Total servicios No Sujetos de IVA" mas "Total mercancías No Sujetas de IVA".
+
+
+
+
+ Se obtiene de la sumatoria de los campos “total gravado”, “total exento”, “Total Exonerado” y “Total No Sujeto
+
+
+
+
+ Se obtiene de la suma de todos los campo de monto de descuento concedido
+
+
+
+
+ Se obtiene de la resta de los campos total venta menos total descuento
+
+
+
+
+ Tipo complejo que contiene los montos desglosados por impuesto cobrado en el comprobante electrónico.
+
+
+
+
+
+ Indicará los códigos de impuesto registrados en las líneas de detalle.
+
+
+
+
+
+ Se obtiene de la sumatoria del monto por código de impuesto cobrado en el comprobante electrónico
+
+
+
+
+
+
+
+ Se obtiene de la suma de todos campos monto del impuesto
+
+
+
+
+ Este campo es de condición obligatoria, cuando existen producto/servicio gravados con algún impuesto en las líneas de detalle que sean asumidos por el emisor
+
+
+
+
+ IVA Devuelto
+
+
+
+
+ Total Otros Cargos
+
+
+
+
+
+
+
+ Corresponde al medio de pago empleado: 01 - Efectivo, 02 - Tarjeta, 03 - Cheque, 04 - Transferencia - depósito bancario, 05 - Recaudado por terceros, 06 - SINPE MOVIL, 07 - Plataforma Digital, 99 - Otros
+
+
+
+
+
+ Efectivo
+
+
+
+
+ Tarjeta
+
+
+
+
+ Cheque
+
+
+
+
+ Transferencia - depósito bancario
+
+
+
+
+ Recaudado por terceros
+
+
+
+
+ SINPE MOVIL
+
+
+
+
+ Plataforma Digital
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de "Otros" de la nota 6. Se debe describir puntualmente el medio de pago utilizado
+
+
+
+
+
+
+
+
+
+
+ Se deberá detallar el monto correspondiente al tipo de pago seleccionado. Se volverá obligatorio cuando se utilice más de un medio de pago.
+
+
+
+
+
+
+
+ Se obtiene de la suma de los campos "total venta neta", "monto total del impuesto" y "total otros cargos" menos "total IVA devuelto", en caso de contar con dichos campos.
+
+
+
+
+
+
+
+
+
+
+ Tipo de documento de referencia
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 10. Se debe describir puntualmente el tipo de documento utilizado
+
+
+
+
+
+
+
+
+
+
+ Clave numérica del comprobante electrónico o consecutivo del documento de referencia
+
+
+
+
+
+
+
+
+
+ Fecha de emisión del documento de referencia
+
+
+
+
+ Código de referencia. 01 Anula documento de referencia, 02 Corrige texto de documento de referencia, 04 Referencia a otro documento, 05 Sustituye comprobante provisional por contigencia, 99 Otros
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 9. Se debe describir puntualmente el código de referencia utilizado
+
+
+
+
+
+
+
+
+
+
+ Razón de referencia
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Elemento opcional que se puede utilizar para almacenar texto.
+
+
+
+
+
+
+ Código opcional para facilitar la identificación del elemento.
+
+
+
+
+
+
+
+
+ Elemento opcional que se puede utilizar para almacenar contenido estructurado.
+
+
+
+
+
+
+ Código opcional para facilitar la identificación del elemento.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nombre o razon social
+
+
+
+
+
+
+
+
+
+
+
+
+ Campo condicional. Se convierte en carácter obligatorio cuando se
+ estén facturando códigosCAByS de bebidas alcohólicas según la Ley
+ 8707. Contiene los datos del número de registro de bebidas
+ alcohólicas, suministrado por la Dirección General de Aduanas
+
+
+
+
+
+
+
+
+
+
+ En caso de que se cuente con nombre comercial debe indicarse
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Debe cumplir con la siguiente estructura:
+ \s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nombre o razon social
+
+
+
+
+
+
+
+
+
+
+
+ En caso de que se cuente con nombre comercial debe indicarse
+
+
+
+
+
+
+
+
+
+
+
+ Campo para incluir la direccion del extranjero, en caso de requerirse.
+
+
+
+
+
+
+
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando el cliente lo requiera. Debe cumplir con la siguiente estructura:
+ \s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo de identificación: 01 Cédula Física, 02 Cédula Jurídica, 03 DIMEX, 04 NITE, 05 Extranjero No Domiciliado, 06 No Contribuyente
+
+
+
+
+
+ Cedula Fisica
+
+
+
+
+ Cedula Juridica
+
+
+
+
+ DIMEX
+
+
+
+
+ NITE
+
+
+
+
+ Extranjero No Domiciliado
+
+
+
+
+ No Contribuyente
+
+
+
+
+
+
+
+ Número de identificación, el contribuyente debe estar inscrito ante la Administración Tributaria
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Código del país
+
+
+
+
+
+
+
+
+
+ Número de teléfono
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo de documento de exoneración o autorización.
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 10.1. Se debe describir puntualmente el tipo de documento o autorización utilizado
+
+
+
+
+
+
+
+
+
+
+ Número de documento de exoneración o autorización
+
+
+
+
+
+
+
+
+
+
+ Número de artículo que establece la exoneración o autorización
+
+
+
+
+
+
+
+
+
+ Número de inciso que establece la exoneración o autorización
+
+
+
+
+
+
+
+
+
+ Nombre de la institución o dependencia que emitió la exoneración
+
+
+
+
+
+
+ Ministerio de Hacienda
+
+
+
+
+ Ministerio de Relaciones Exteriores y Culto
+
+
+
+
+ Ministerio de Agricultura y Ganadería
+
+
+
+
+ Ministerio de Economía, Industria y Comercio
+
+
+
+
+ Cruz Roja Costarricense
+
+
+
+
+ Benemérito Cuerpo de Bomberos de Costa Rica
+
+
+
+
+ Asociación Obras del Espíritu Santo
+
+
+
+
+ Federación Cruzada Nacional de protección al Anciano (Fecrunapa)
+
+
+
+
+ Escuela de Agricultura de la Región Húmeda (EARTH)
+
+
+
+
+ Instituto Centroamericano de Administración de Empresas (INCAE)
+
+
+
+
+ Junta de Protección Social (JPS)
+
+
+
+
+ Autoridad Reguladora de los Servicios Públicos (Aresep)
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+ Detalle Nombre de institución o dependencia que emitió la exoneración OTRO
+
+
+
+
+
+
+
+
+
+
+ Fecha y hora de la emisión del documento de exoneración o autorización.
+
+
+
+
+ Tarifa exonerada
+
+
+
+
+
+
+
+
+
+
+ Monto del impuesto exonerado
+
+
+
+
+
+
+
+
+
+ Código del impuesto:
+ 01 Impuesto al valor agregado,
+ 02 Impuesto Selectivo de Consumo,
+ 03 Impuesto único a los combustivos,
+ 04 Impuesto específico de bebidas alcohólicas,
+ 05 Impuesto específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador,
+ 06 Impuesto a los productos de tabaco,
+ 07 IVA (cálculo especial),
+ 08 IVA Regimen de Bienes Usados (Factor),
+ 12 Impuesto Especifico al cemento,
+ 99 Otros
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 8.
+ Se debe describir puntualmente el impuesto utilizado
+
+
+
+
+
+
+
+
+
+
+
+
+ En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”,
+ no se deberá utilizar este campo, ya que el impuesto se calcula como la suma de los
+ montos de impuestos individuales de las líneas de detalle de los componentes del surtido
+ que se deben incluir en estos casos. La eventual validación de la consistencia de los
+ impuestos calculados y aplicación de tarifas se hará sobre las líneas individuales de detalle.
+
+
+
+
+
+
+ En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”,
+ no se deberá utilizar este campo, ya que el impuesto se calcula como la suma de los montos
+ de impuestos individuales de las líneas de detalle de componentes del surtido que se
+ deben incluir en estos casos. La eventual validación de la consistencia de los
+ impuestos calculados y aplicación de tarifas se hará sobre las líneas individuales de detalle.
+
+
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando el producto/servicio posea un factor para su cálculo.
+ Cuando en el código de impuesto se defina IVA Bienes Usados se deberá utilizar este campo con el factor establecido por el Ministerio de Hacienda
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo complejo con el detalle para calcular impuestos específicos no tarifarios. Este campo es de condición obligatoria, cuando se utilicen
+ los códigos de impuesto 03, 04, 05, 06 de la nota 8 y agrupará los campos requeridos para el cálculo de estos impuestos
+
+
+
+
+
+
+
+ En el caso que se utilice el nodo “Detalle de productos del surtido, paquetes o combos”, no se deberá utilizar este
+ campo, para los códigos de impuesto 04, 05, 06 de la nota 8, ya que el impuesto se calcula como la suma de los montos de impuestos individuales de las líneas de detalle de
+ componentes del surtido que se deben incluir en estos casos
+
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilice el código de impuesto 04 de la nota 8 Este campo se obtiene de multiplicar la “Cantidad de la unidad de medida a utilizar” por el “Porcentaje”
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilice el código de impuesto 05 de la nota 8
+
+
+
+
+
+
+
+
+
+
+ Este campo es de condición obligatoria, cuando se utilicen los códigos de impuesto 03, 04, 05 y 06 de la nota 8
+
+
+
+
+
+
+
+ Monto del impuesto
+
+
+
+
+
+
+
+
+
+
+ Será obligatorio para las líneas de detalle que utilicen uno de
+ los códigos de producto/servicio de "surtidos" que estén
+ habilitados en el CAByS.
+ En el caso de la inclusión de paquetes, surtidos o combos,
+ entendidos como la combinación de más de dos productos
+ con diferentes códigos de producto/servicio, se debe
+ seleccionar el código 03 "Código del producto asignado por la
+ industria" de la nota 12 e incluir en el campo "código" el
+ respectivo código "SKU", GTIN o equivalentes, con el que el
+ paquete este identificado en la industria. Estos códigos deben
+ ser verificables en los catálogos disponibles en la industria.
+
+
+
+
+
+
+
+
+ Código del producto del vendedor
+
+
+
+
+ Código del producto del comprador
+
+
+
+
+ código del producto asignado por la industria
+
+
+
+
+ código de uso interno
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+
+ Será obligatorio para las líneas de detalle que utilicen uno de
+ los códigos de producto/servicio de "surtidos" que estén
+ habilitados en el CAByS.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Monto de descuento concedido. Este campo será de condición obligatoria, cuando se indique
+ un descuento, en el campo "Código del descuento"
+
+
+
+
+
+
+ Este campo será de condición obligatoria, cuando se incluya
+ información en el campo "monto de descuentos concedidos"
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de "Otros"
+ de la nota 20. Se debe describir puntualmente el descuento
+ utilizado
+
+
+
+
+
+
+
+
+
+
+
+ Naturaleza del descuento, que es obligatorio si existe descuento
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Se verificará el cumplimiento de la nota 16.
+ Además, cuando se seleccione el código 04, 08, 09 y 10 de la nota 16 en “Tipo de documento otros cargos” y no se cuente con una línea de servicio o producto, no es obligatorio usar el nodo “Detalle de la mercancía o servicio prestado”.
+
+
+
+
+
+
+
+
+
+
+
+ Será obligatorio en caso de utilizar el código 99 de “Otros” de la nota 16. Se debe describir puntualmente el tipo de documento utilizado
+
+
+
+
+
+
+
+
+
+
+
+ Nombre o razón social del receptor
+
+
+
+
+
+
+
+
+
+
+ Detalle de otros cargos
+
+
+
+
+
+
+
+
+
+ En el caso que el cargo posea un porcentaje o monto para su cálculo se debe de indicar el mismo
+
+
+
+
+
+
+
+
+
+
+
+ Monto del cargo
+
+
+
+
+
+
+
+
+ Código de la moneda
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo de cambio
+
+
+
+
+
+
+ Tipo de dato decimal para representar los valores de dinero.
+
+
+
+
+
+
+
+
+
+
+ Tipo de dato String que solo permite el uso de números con un largo de 50.
+
+
+
+
+
+
+
+ Tipo de dato String que solo permite el uso de números con un largo de 20
+
+
+
+
+
+
+
+ Unidades de Medida basadas en el estándar RTC 443:2010
+
+
+
+
+ uno (índice de refracción)
+
+
+
+
+ minuto
+
+
+
+
+ segundo
+
+
+
+
+ grado Celsius
+
+
+
+
+ 1 por metro
+
+
+
+
+ Ampere
+
+
+
+
+ ampere por metro
+
+
+
+
+ ampere por metro cuadrado
+
+
+
+
+ Activo Virtual
+
+
+
+
+ Alquiler de uso habitacional
+
+
+
+
+ Alquiler de uso comercial
+
+
+
+
+ bel
+
+
+
+
+ Becquerel
+
+
+
+
+ coulomb
+
+
+
+
+ coulomb por kilogramo
+
+
+
+
+ coulomb por metro cuadrado
+
+
+
+
+ coulomb por metro cúbico
+
+
+
+
+ Cajuela de café
+
+
+
+
+ Candela
+
+
+
+
+ candela por metro cuadrado
+
+
+
+
+ Comisiones
+
+
+
+
+ centímetro
+
+
+
+
+ cuartillos de café
+
+
+
+
+ día
+
+
+
+
+ electronvolt
+
+
+
+
+ farad
+
+
+
+
+ farad por metro
+
+
+
+
+ fanega de café
+
+
+
+
+ Gramo
+
+
+
+
+ Galón
+
+
+
+
+ gray
+
+
+
+
+ gray por segundo
+
+
+
+
+ hora
+
+
+
+
+ henry
+
+
+
+
+ henry por metro
+
+
+
+
+ hertz
+
+
+
+
+ Intereses
+
+
+
+
+ Joule
+
+
+
+
+ joule por kilogramo kelvin
+
+
+
+
+ joule por mol kelvin
+
+
+
+
+ joule por kelvin
+
+
+
+
+ joule por kilogramo
+
+
+
+
+ joule por metro cúbico
+
+
+
+
+ joule por mol
+
+
+
+
+ Kelvin
+
+
+
+
+ katal
+
+
+
+
+ katal por metro cúbico
+
+
+
+
+ Kilogramo
+
+
+
+
+ kilogramo por metro cúbico
+
+
+
+
+ Kilometro
+
+
+
+
+ kilovatios
+
+
+
+
+ Kilovatios por hora
+
+
+
+
+ litro
+
+
+
+
+ lumen
+
+
+
+
+ pulgada
+
+
+
+
+ lux
+
+
+
+
+ Metro
+
+
+
+
+ metro por segundo
+
+
+
+
+ metro por segundo cuadrado
+
+
+
+
+ metro cuadrado
+
+
+
+
+ metro cúbico
+
+
+
+
+ minuto
+
+
+
+
+ mililitro
+
+
+
+
+ Milímetro
+
+
+
+
+ Mol
+
+
+
+
+ mol por metro cúbico
+
+
+
+
+ newton
+
+
+
+
+ newton por metro
+
+
+
+
+ newton metro
+
+
+
+
+ neper
+
+
+
+
+ grado
+
+
+
+
+ Otro tipo de servicio
+
+
+
+
+ Se debe indicar la descripción de la medida a utilizar
+
+
+
+
+ Onzas
+
+
+
+
+ pascal
+
+
+
+
+ pascal segundo
+
+
+
+
+ Quintal
+
+
+
+
+ radián
+
+
+
+
+ radián por segundo
+
+
+
+
+ radián por segundo cuadrado
+
+
+
+
+ Segundo
+
+
+
+
+ siemens
+
+
+
+
+ Servicios Profesionales
+
+
+
+
+ Servicios personales
+
+
+
+
+ estereorradián
+
+
+
+
+ Servicios técnicos
+
+
+
+
+ sievert
+
+
+
+
+ tesla
+
+
+
+
+ tonelada
+
+
+
+
+ unidad de masa atómica unificada
+
+
+
+
+ unidad astronómica
+
+
+
+
+ Unidad
+
+
+
+
+ volt
+
+
+
+
+ volt por metro
+
+
+
+
+ Watt
+
+
+
+
+ watt por metro kevin
+
+
+
+
+ watt por metro cuadrado estereorradián
+
+
+
+
+ watt por metro cuadrado
+
+
+
+
+ watt por estereorradián
+
+
+
+
+ weber
+
+
+
+
+ ohm
+
+
+
+
+
+
+ Tipos de transacción para el caso de transacciones con tratamientos tributarios específicos
+
+
+
+
+ Venta Normal de Bienes y Servicios (Transacción General)
+
+
+
+
+ Mercancía de Autoconsumo exento
+
+
+
+
+ Mercancía de Autoconsumo gravado
+
+
+
+
+ Servicio de Autoconsumo exento
+
+
+
+
+ Servicio de Autoconsumo gravado
+
+
+
+
+ Cuota de afiliación
+
+
+
+
+ Cuota de afiliación Exenta
+
+
+
+
+ Bienes de Capital para el emisor
+
+
+
+
+ Bienes de Capital para el receptor
+
+
+
+
+ Bienes de Capital para el emisor y el receptor
+
+
+
+
+ Bienes de capital de autoconsumo exento para el emisor
+
+
+
+
+ Bienes de capital sin contraprestación a terceros exento para el emisor
+
+
+
+
+ Sin contraprestación a terceros
+
+
+
+
+
+
+ Los tipos de descuentos a utilizar en el campo "Código del Descuento" del Descuento
+
+
+
+
+
+
+ Descuento por Regalía
+
+
+
+
+ Descuento por Regalía IVA Cobrado al Cliente
+
+
+
+
+ Descuento por Bonificación
+
+
+
+
+ Descuento por volumen
+
+
+
+
+ Descuento por Temporada (estacional)
+
+
+
+
+ Descuento promocional
+
+
+
+
+ Descuento Comercial
+
+
+
+
+ Descuento por frecuencia
+
+
+
+
+ Descuento sostenido
+
+
+
+
+ Otros descuentos
+
+
+
+
+
+
+
+ Código del impuesto:
+ 01 Impuesto al valor agregado,
+ 02 Impuesto Selectivo de Consumo,
+ 03 Impuesto único a los combustivos,
+ 04 Impuesto específico de bebidas alcohólicas,
+ 05 Impuesto específico sobre las bebidas envasadas sin contenido alcohólico y jabones de tocador,
+ 06 Impuesto a los productos de tabaco,
+ 07 IVA (cálculo especial),
+ 08 IVA Regimen de Bienes Usados (Factor),
+ 12 Impuesto Especifico al cemento,
+ 99 Otros
+
+
+
+
+
+
+
+ Impuesto al Valor Agregado
+
+
+
+
+ Impuesto Selectivo de Consumo
+
+
+
+
+ Impuesto unico a los combustivos
+
+
+
+
+ Impuesto especifico de bebidas alcohólicas
+
+
+
+
+ impuesto especifico sobre las bebidas envasadas sin contenido alcoholico y jabones de tocador
+
+
+
+
+ impuesto a los productos de tabaco
+
+
+
+
+ IVA (cálculo especial)
+
+
+
+
+ IVA Régimen de Bienes Usados (Factor)
+
+
+
+
+ Impuesto Especifico al Cemento
+
+
+
+
+ Otros
+
+
+
+
+
+
+ Cuando se trata del IVA las tarifas y códigos a utilizar son las siguientes
+
+
+
+
+
+
+ Tarifa 0% (Artículo 32, num 1, RLIVA)
+
+
+
+
+ Tarifa reducida 1%
+
+
+
+
+ Tarifa reducida 2%
+
+
+
+
+ Tarifa reducida 4%
+
+
+
+
+ Transitorio 0%
+
+
+
+
+ Transitorio 4%
+
+
+
+
+ Tarifa transitoria 8%
+
+
+
+
+ Tarifa general 13%
+
+
+
+
+ Tarifa reducida 0.5%
+
+
+
+
+ Tarifa Exenta
+
+
+
+
+ Tarifa 0% sin derecho a crédito
+
+
+
+
+
+
+ Tipo de documento de exoneración o de autorización.
+
+
+
+
+
+
+ Compras autorizadas por la Dirección General de Tributación
+
+
+
+
+ Ventas exentas a diplomáticos
+
+
+
+
+ Autorizado por Ley Especial
+
+
+
+
+ Exenciones Dirección General de Hacienda Autorización Local Genérica
+
+
+
+
+
+ Exenciones Dirección General de Hacienda Transitorio V (servicios de ingeniería, arquitectura, topografía obra civil)
+
+
+
+
+
+ Servicios turísticos inscritos ante el Instituto Costarricense de Turismo (ICT)
+
+
+
+
+ Transitorio XVII (Recolección, Clasificación, almacenamiento de Reciclaje y reutilizable)
+
+
+
+
+ Exoneración a Zona Franca
+
+
+
+
+ Exoneración de servicios complementarios para la exportación articulo 11 RLIVA
+
+
+
+
+ Órgano de las corporaciones municipales
+
+
+
+
+ Exenciones Dirección General de Hacienda Autorización de Impuesto Local Concreta
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+ Tipo de documento otros cargos
+
+
+
+
+
+ Contribución parafiscal
+
+
+
+
+ Timbre de la Cruz Roja
+
+
+
+
+ Timbre de Benemérito Cuerpo de Bomberos de Costa Rica
+
+
+
+
+ Cobro de un tercero
+
+
+
+
+ Costos de Exportación
+
+
+
+
+ Impuesto de servicio 10%
+
+
+
+
+ Timbre de Colegios Profesionales
+
+
+
+
+ Depósitos de Garantía
+
+
+
+
+ Multas o Penalizaciones
+
+
+
+
+ Intereses Moratorios
+
+
+
+
+ Otros Cargos
+
+
+
+
+
+
+
+
+
+
+ Factura electrónica
+
+
+
+
+ Nota de debido electrónica
+
+
+
+
+ nota de crédito electrónica
+
+
+
+
+ Tiquete electrónico
+
+
+
+
+ Nota de despacho
+
+
+
+
+ Contrato
+
+
+
+
+ Procedimiento
+
+
+
+
+ Comprobante emitido en contigencia
+
+
+
+
+ Devolución mercadería
+
+
+
+
+ Comprobante electrónico rechazado por el Ministerio de Hacienda
+
+
+
+
+ Sustituye factura rechazada por el Receptor del comprobante
+
+
+
+
+ Sustituye Factura de exportación
+
+
+
+
+ Facturación mes vencido
+
+
+
+
+ Otros
+
+
+
+
+ Comprobante aportado por contribuyente de Régimen Especial.
+
+
+
+
+ Sustituye una Factura electrónica de Compra
+
+
+
+
+ Comprobante de Proveedor No Domiciliado
+
+
+
+
+ Nota de Crédito a Factura Electrónica de Compra
+
+
+
+
+ Nota de Débito a Factura Electrónica de Compra
+
+
+
+
+
+
+
+
+
+
+ Anula documento de referencia
+
+
+
+
+ Corrige texto de ocumento de referencia
+
+
+
+
+ Referencia a otro documento
+
+
+
+
+ Sustituye comprobante provisional por contigencia
+
+
+
+
+ Devolución de mercancía
+
+
+
+
+ Sustituye comprobante electrónico
+
+
+
+
+ Factura Endosada
+
+
+
+
+ Nota de crédito financiera
+
+
+
+
+ Nota de débito financiera
+
+
+
+
+ Proveedor No Domiciliado
+
+
+
+
+ Crédito por exoneración posterior a la facturación
+
+
+
+
+ Otros
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/www/xsd/NotaCreditoElectronica_V4.4.xsd b/www/xsd/NotaCreditoElectronica_V4.4.xsd
index a0d240c7..7bd8c0f5 100644
--- a/www/xsd/NotaCreditoElectronica_V4.4.xsd
+++ b/www/xsd/NotaCreditoElectronica_V4.4.xsd
@@ -978,7 +978,6 @@ se obtiene de la sumatoria de los campos “Subtotal”, “Impuesto Neto”.
-
diff --git a/www/xsd/TiqueteElectronico_V4.4.xsd b/www/xsd/TiqueteElectronico_V4.4.xsd
index 6743e886..03e767c3 100644
--- a/www/xsd/TiqueteElectronico_V4.4.xsd
+++ b/www/xsd/TiqueteElectronico_V4.4.xsd
@@ -929,7 +929,6 @@ se obtiene de la sumatoria de los campos “Subtotal”, “Impuesto Neto”.
-